diff --git a/backend/src/hatchling/__about__.py b/backend/src/hatchling/__about__.py index e2ba8ba3b..9813fabb0 100644 --- a/backend/src/hatchling/__about__.py +++ b/backend/src/hatchling/__about__.py @@ -1 +1 @@ -__version__ = '1.27.0' +__version__ = "1.27.0" diff --git a/backend/src/hatchling/__main__.py b/backend/src/hatchling/__main__.py index b528284b9..c81c05dcd 100644 --- a/backend/src/hatchling/__main__.py +++ b/backend/src/hatchling/__main__.py @@ -1,6 +1,6 @@ import sys -if __name__ == '__main__': +if __name__ == "__main__": from hatchling.cli import hatchling sys.exit(hatchling()) diff --git a/backend/src/hatchling/bridge/app.py b/backend/src/hatchling/bridge/app.py index b54d110d6..bcafa4294 100644 --- a/backend/src/hatchling/bridge/app.py +++ b/backend/src/hatchling/bridge/app.py @@ -15,7 +15,7 @@ class Application: """ def __init__(self) -> None: - self.__verbosity = int(os.environ.get('HATCH_VERBOSE', '0')) - int(os.environ.get('HATCH_QUIET', '0')) + self.__verbosity = int(os.environ.get("HATCH_VERBOSE", "0")) - int(os.environ.get("HATCH_QUIET", "0")) @property def verbosity(self) -> int: @@ -25,62 +25,62 @@ def verbosity(self) -> int: return self.__verbosity @staticmethod - def display(message: str = '', **kwargs: Any) -> None: # noqa: ARG004 + def display(message: str = "", **kwargs: Any) -> None: # noqa: ARG004 # Do not document _display(message, always=True) - def display_info(self, message: str = '', **kwargs: Any) -> None: # noqa: ARG002 + def display_info(self, message: str = "", **kwargs: Any) -> None: # noqa: ARG002 """ Meant to be used for messages conveying basic information. """ if self.__verbosity >= 0: _display(message) - def display_waiting(self, message: str = '', **kwargs: Any) -> None: # noqa: ARG002 + def display_waiting(self, message: str = "", **kwargs: Any) -> None: # noqa: ARG002 """ Meant to be used for messages shown before potentially time consuming operations. """ if self.__verbosity >= 0: _display(message) - def display_success(self, message: str = '', **kwargs: Any) -> None: # noqa: ARG002 + def display_success(self, message: str = "", **kwargs: Any) -> None: # noqa: ARG002 """ Meant to be used for messages indicating some positive outcome. """ if self.__verbosity >= 0: _display(message) - def display_warning(self, message: str = '', **kwargs: Any) -> None: # noqa: ARG002 + def display_warning(self, message: str = "", **kwargs: Any) -> None: # noqa: ARG002 """ Meant to be used for messages conveying important information. """ if self.__verbosity >= -1: _display(message) - def display_error(self, message: str = '', **kwargs: Any) -> None: # noqa: ARG002 + def display_error(self, message: str = "", **kwargs: Any) -> None: # noqa: ARG002 """ Meant to be used for messages indicating some unrecoverable error. """ if self.__verbosity >= -2: # noqa: PLR2004 _display(message) - def display_debug(self, message: str = '', level: int = 1, **kwargs: Any) -> None: # noqa: ARG002 + def display_debug(self, message: str = "", level: int = 1, **kwargs: Any) -> None: # noqa: ARG002 """ Meant to be used for messages that are not useful for most user experiences. The `level` option must be between 1 and 3 (inclusive). """ if not 1 <= level <= 3: # noqa: PLR2004 - error_message = 'Debug output can only have verbosity levels between 1 and 3 (inclusive)' + error_message = "Debug output can only have verbosity levels between 1 and 3 (inclusive)" raise ValueError(error_message) if self.__verbosity >= level: _display(message) - def display_mini_header(self, message: str = '', **kwargs: Any) -> None: # noqa: ARG002 + def display_mini_header(self, message: str = "", **kwargs: Any) -> None: # noqa: ARG002 if self.__verbosity >= 0: - _display(f'[{message}]') + _display(f"[{message}]") - def abort(self, message: str = '', code: int = 1, **kwargs: Any) -> None: # noqa: ARG002 + def abort(self, message: str = "", code: int = 1, **kwargs: Any) -> None: # noqa: ARG002 """ Terminate the program with the given return code. """ diff --git a/backend/src/hatchling/build.py b/backend/src/hatchling/build.py index 5a1da4f89..8faaeb4f4 100644 --- a/backend/src/hatchling/build.py +++ b/backend/src/hatchling/build.py @@ -4,14 +4,14 @@ from typing import Any __all__ = [ - 'build_editable', - 'build_sdist', - 'build_wheel', - 'get_requires_for_build_editable', - 'get_requires_for_build_sdist', - 'get_requires_for_build_wheel', + "build_editable", + "build_sdist", + "build_wheel", + "get_requires_for_build_editable", + "get_requires_for_build_sdist", + "get_requires_for_build_wheel", ] -__all__ += ['__all__'] +__all__ += ["__all__"] def get_requires_for_build_sdist(config_settings: dict[str, Any] | None = None) -> list[str]: # noqa: ARG001 @@ -31,7 +31,7 @@ def build_sdist(sdist_directory: str, config_settings: dict[str, Any] | None = N from hatchling.builders.sdist import SdistBuilder builder = SdistBuilder(os.getcwd()) - return os.path.basename(next(builder.build(directory=sdist_directory, versions=['standard']))) + return os.path.basename(next(builder.build(directory=sdist_directory, versions=["standard"]))) def get_requires_for_build_wheel(config_settings: dict[str, Any] | None = None) -> list[str]: # noqa: ARG001 @@ -55,7 +55,7 @@ def build_wheel( from hatchling.builders.wheel import WheelBuilder builder = WheelBuilder(os.getcwd()) - return os.path.basename(next(builder.build(directory=wheel_directory, versions=['standard']))) + return os.path.basename(next(builder.build(directory=wheel_directory, versions=["standard"]))) def get_requires_for_build_editable(config_settings: dict[str, Any] | None = None) -> list[str]: # noqa: ARG001 @@ -80,7 +80,7 @@ def build_editable( from hatchling.builders.wheel import WheelBuilder builder = WheelBuilder(os.getcwd()) - return os.path.basename(next(builder.build(directory=wheel_directory, versions=['editable']))) + return os.path.basename(next(builder.build(directory=wheel_directory, versions=["editable"]))) # Any builder that has build-time hooks like Hatchling and setuptools cannot technically keep PEP 517's identical @@ -95,8 +95,8 @@ def build_editable( # There are legitimate use cases in which this is required, so we only define these when no pip build is detected. # See: https://github.com/pypa/pip/blob/22.2.2/src/pip/_internal/operations/build/build_tracker.py#L41-L51 # Example use case: https://github.com/pypa/hatch/issues/532 -if 'PIP_BUILD_TRACKER' not in os.environ: - __all__ += ['prepare_metadata_for_build_editable', 'prepare_metadata_for_build_wheel'] +if "PIP_BUILD_TRACKER" not in os.environ: + __all__ += ["prepare_metadata_for_build_editable", "prepare_metadata_for_build_wheel"] def prepare_metadata_for_build_wheel( metadata_directory: str, @@ -109,11 +109,11 @@ def prepare_metadata_for_build_wheel( builder = WheelBuilder(os.getcwd()) - directory = os.path.join(metadata_directory, f'{builder.artifact_project_id}.dist-info') + directory = os.path.join(metadata_directory, f"{builder.artifact_project_id}.dist-info") if not os.path.isdir(directory): os.mkdir(directory) - with open(os.path.join(directory, 'METADATA'), 'w', encoding='utf-8') as f: + with open(os.path.join(directory, "METADATA"), "w", encoding="utf-8") as f: f.write(builder.config.core_metadata_constructor(builder.metadata)) return os.path.basename(directory) @@ -130,7 +130,7 @@ def prepare_metadata_for_build_editable( builder = WheelBuilder(os.getcwd()) - directory = os.path.join(metadata_directory, f'{builder.artifact_project_id}.dist-info') + directory = os.path.join(metadata_directory, f"{builder.artifact_project_id}.dist-info") if not os.path.isdir(directory): os.mkdir(directory) @@ -138,7 +138,7 @@ def prepare_metadata_for_build_editable( if not builder.config.dev_mode_dirs and builder.config.dev_mode_exact: extra_dependencies.append(EDITABLES_REQUIREMENT) - with open(os.path.join(directory, 'METADATA'), 'w', encoding='utf-8') as f: + with open(os.path.join(directory, "METADATA"), "w", encoding="utf-8") as f: f.write(builder.config.core_metadata_constructor(builder.metadata, extra_dependencies=extra_dependencies)) return os.path.basename(directory) diff --git a/backend/src/hatchling/builders/app.py b/backend/src/hatchling/builders/app.py index 59bb93285..bcd640c3e 100644 --- a/backend/src/hatchling/builders/app.py +++ b/backend/src/hatchling/builders/app.py @@ -6,7 +6,7 @@ class AppBuilder(BinaryBuilder): - PLUGIN_NAME = 'app' + PLUGIN_NAME = "app" def build_bootstrap( self, @@ -14,7 +14,7 @@ def build_bootstrap( **build_data: Any, ) -> str: self.app.display_warning( - 'The `app` build target is deprecated and will be removed in a future release. ' - 'Use the `binary` build target instead.' + "The `app` build target is deprecated and will be removed in a future release. " + "Use the `binary` build target instead." ) return super().build_bootstrap(directory, **build_data) diff --git a/backend/src/hatchling/builders/binary.py b/backend/src/hatchling/builders/binary.py index af345c9f2..a85afa835 100644 --- a/backend/src/hatchling/builders/binary.py +++ b/backend/src/hatchling/builders/binary.py @@ -9,7 +9,7 @@ class BinaryBuilderConfig(BuilderConfig): - SUPPORTED_VERSIONS = ('3.12', '3.11', '3.10', '3.9', '3.8', '3.7') + SUPPORTED_VERSIONS = ("3.12", "3.11", "3.10", "3.9", "3.8", "3.7") def __init__(self, *args: Any, **kwargs: Any) -> None: super().__init__(*args, **kwargs) @@ -22,21 +22,21 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: def scripts(self) -> list[str]: if self.__scripts is None: known_scripts = self.builder.metadata.core.scripts - scripts = self.target_config.get('scripts', []) + scripts = self.target_config.get("scripts", []) if not isinstance(scripts, list): - message = f'Field `tool.hatch.build.targets.{self.plugin_name}.scripts` must be an array' + message = f"Field `tool.hatch.build.targets.{self.plugin_name}.scripts` must be an array" raise TypeError(message) for i, script in enumerate(scripts, 1): if not isinstance(script, str): message = ( - f'Script #{i} of field `tool.hatch.build.targets.{self.plugin_name}.scripts` must be a string' + f"Script #{i} of field `tool.hatch.build.targets.{self.plugin_name}.scripts` must be a string" ) raise TypeError(message) if script not in known_scripts: - message = f'Unknown script in field `tool.hatch.build.targets.{self.plugin_name}.scripts`: {script}' + message = f"Unknown script in field `tool.hatch.build.targets.{self.plugin_name}.scripts`: {script}" raise ValueError(message) self.__scripts = sorted(set(scripts)) if scripts else list(known_scripts) @@ -46,19 +46,19 @@ def scripts(self) -> list[str]: @property def python_version(self) -> str: if self.__python_version is None: - python_version = self.target_config.get('python-version', '') + python_version = self.target_config.get("python-version", "") if not isinstance(python_version, str): - message = f'Field `tool.hatch.build.targets.{self.plugin_name}.python-version` must be a string' + message = f"Field `tool.hatch.build.targets.{self.plugin_name}.python-version` must be a string" raise TypeError(message) - if not python_version and 'PYAPP_DISTRIBUTION_SOURCE' not in os.environ: + if not python_version and "PYAPP_DISTRIBUTION_SOURCE" not in os.environ: for supported_version in self.SUPPORTED_VERSIONS: if self.builder.metadata.core.python_constraint.contains(supported_version): python_version = supported_version break else: - message = 'Field `project.requires-python` is incompatible with the known distributions' + message = "Field `project.requires-python` is incompatible with the known distributions" raise ValueError(message) self.__python_version = python_version @@ -68,10 +68,10 @@ def python_version(self) -> str: @property def pyapp_version(self) -> str: if self.__pyapp_version is None: - pyapp_version = self.target_config.get('pyapp-version', '') + pyapp_version = self.target_config.get("pyapp-version", "") if not isinstance(pyapp_version, str): - message = f'Field `tool.hatch.build.targets.{self.plugin_name}.pyapp-version` must be a string' + message = f"Field `tool.hatch.build.targets.{self.plugin_name}.pyapp-version` must be a string" raise TypeError(message) self.__pyapp_version = pyapp_version @@ -84,13 +84,13 @@ class BinaryBuilder(BuilderInterface): Build binaries """ - PLUGIN_NAME = 'binary' + PLUGIN_NAME = "binary" def get_version_api(self) -> dict[str, Callable]: - return {'bootstrap': self.build_bootstrap} + return {"bootstrap": self.build_bootstrap} def get_default_versions(self) -> list[str]: # noqa: PLR6301 - return ['bootstrap'] + return ["bootstrap"] def clean( self, @@ -111,73 +111,73 @@ def build_bootstrap( import shutil import tempfile - cargo_path = os.environ.get('CARGO', '') + cargo_path = os.environ.get("CARGO", "") if not cargo_path: - if not shutil.which('cargo'): - message = 'Executable `cargo` could not be found on PATH' + if not shutil.which("cargo"): + message = "Executable `cargo` could not be found on PATH" raise OSError(message) - cargo_path = 'cargo' + cargo_path = "cargo" app_dir = os.path.join(directory, self.PLUGIN_NAME) if not os.path.isdir(app_dir): os.makedirs(app_dir) - on_windows = sys.platform == 'win32' + on_windows = sys.platform == "win32" base_env = dict(os.environ) - base_env['PYAPP_PROJECT_NAME'] = self.metadata.name - base_env['PYAPP_PROJECT_VERSION'] = self.metadata.version + base_env["PYAPP_PROJECT_NAME"] = self.metadata.name + base_env["PYAPP_PROJECT_VERSION"] = self.metadata.version if self.config.python_version: - base_env['PYAPP_PYTHON_VERSION'] = self.config.python_version + base_env["PYAPP_PYTHON_VERSION"] = self.config.python_version # https://doc.rust-lang.org/cargo/reference/config.html#buildtarget - build_target = os.environ.get('CARGO_BUILD_TARGET', '') + build_target = os.environ.get("CARGO_BUILD_TARGET", "") # This will determine whether we install from crates.io or build locally and is currently required for # cross compilation: https://github.com/cross-rs/cross/issues/1215 - repo_path = os.environ.get('PYAPP_REPO', '') + repo_path = os.environ.get("PYAPP_REPO", "") with tempfile.TemporaryDirectory() as temp_dir: - exe_name = 'pyapp.exe' if on_windows else 'pyapp' + exe_name = "pyapp.exe" if on_windows else "pyapp" if repo_path: context_dir = repo_path - target_dir = os.path.join(temp_dir, 'build') + target_dir = os.path.join(temp_dir, "build") if build_target: - temp_exe_path = os.path.join(target_dir, build_target, 'release', exe_name) + temp_exe_path = os.path.join(target_dir, build_target, "release", exe_name) else: - temp_exe_path = os.path.join(target_dir, 'release', exe_name) - install_command = [cargo_path, 'build', '--release', '--target-dir', target_dir] + temp_exe_path = os.path.join(target_dir, "release", exe_name) + install_command = [cargo_path, "build", "--release", "--target-dir", target_dir] else: context_dir = temp_dir - temp_exe_path = os.path.join(temp_dir, 'bin', exe_name) - install_command = [cargo_path, 'install', 'pyapp', '--force', '--root', temp_dir] + temp_exe_path = os.path.join(temp_dir, "bin", exe_name) + install_command = [cargo_path, "install", "pyapp", "--force", "--root", temp_dir] if self.config.pyapp_version: - install_command.extend(['--version', self.config.pyapp_version]) + install_command.extend(["--version", self.config.pyapp_version]) if self.config.scripts: for script in self.config.scripts: env = dict(base_env) - env['PYAPP_EXEC_SPEC'] = self.metadata.core.scripts[script] + env["PYAPP_EXEC_SPEC"] = self.metadata.core.scripts[script] self.cargo_build(install_command, cwd=context_dir, env=env) exe_stem = ( - f'{script}-{self.metadata.version}-{build_target}' + f"{script}-{self.metadata.version}-{build_target}" if build_target - else f'{script}-{self.metadata.version}' + else f"{script}-{self.metadata.version}" ) - exe_path = os.path.join(app_dir, f'{exe_stem}.exe' if on_windows else exe_stem) + exe_path = os.path.join(app_dir, f"{exe_stem}.exe" if on_windows else exe_stem) shutil.move(temp_exe_path, exe_path) else: self.cargo_build(install_command, cwd=context_dir, env=base_env) exe_stem = ( - f'{self.metadata.name}-{self.metadata.version}-{build_target}' + f"{self.metadata.name}-{self.metadata.version}-{build_target}" if build_target - else f'{self.metadata.name}-{self.metadata.version}' + else f"{self.metadata.name}-{self.metadata.version}" ) - exe_path = os.path.join(app_dir, f'{exe_stem}.exe' if on_windows else exe_stem) + exe_path = os.path.join(app_dir, f"{exe_stem}.exe" if on_windows else exe_stem) shutil.move(temp_exe_path, exe_path) return app_dir @@ -186,12 +186,12 @@ def cargo_build(self, *args: Any, **kwargs: Any) -> None: import subprocess if self.app.verbosity < 0: - kwargs['stdout'] = subprocess.PIPE - kwargs['stderr'] = subprocess.STDOUT + kwargs["stdout"] = subprocess.PIPE + kwargs["stderr"] = subprocess.STDOUT process = subprocess.run(*args, **kwargs) # noqa: PLW1510 if process.returncode: - message = f'Compilation failed (code {process.returncode})' + message = f"Compilation failed (code {process.returncode})" raise OSError(message) @classmethod diff --git a/backend/src/hatchling/builders/config.py b/backend/src/hatchling/builders/config.py index 06d8cc33d..f33ba4ef7 100644 --- a/backend/src/hatchling/builders/config.py +++ b/backend/src/hatchling/builders/config.py @@ -111,32 +111,32 @@ def directory_is_excluded(self, name: str, relative_path: str) -> bool: return ( self.path_is_reserved(relative_directory) # The trailing slash is necessary so e.g. `bar/` matches `foo/bar` - or (self.skip_excluded_dirs and self.path_is_excluded(f'{relative_directory}/')) + or (self.skip_excluded_dirs and self.path_is_excluded(f"{relative_directory}/")) ) @cached_property def include_spec(self) -> pathspec.GitIgnoreSpec | None: - if 'include' in self.target_config: + if "include" in self.target_config: include_config = self.target_config - include_location = f'tool.hatch.build.targets.{self.plugin_name}.include' + include_location = f"tool.hatch.build.targets.{self.plugin_name}.include" else: include_config = self.build_config - include_location = 'tool.hatch.build.include' + include_location = "tool.hatch.build.include" all_include_patterns = [] - include_patterns = include_config.get('include', self.default_include()) + include_patterns = include_config.get("include", self.default_include()) if not isinstance(include_patterns, list): - message = f'Field `{include_location}` must be an array of strings' + message = f"Field `{include_location}` must be an array of strings" raise TypeError(message) for i, include_pattern in enumerate(include_patterns, 1): if not isinstance(include_pattern, str): - message = f'Pattern #{i} in field `{include_location}` must be a string' + message = f"Pattern #{i} in field `{include_location}` must be a string" raise TypeError(message) if not include_pattern: - message = f'Pattern #{i} in field `{include_location}` cannot be an empty string' + message = f"Pattern #{i} in field `{include_location}` cannot be an empty string" raise ValueError(message) all_include_patterns.append(include_pattern) @@ -151,30 +151,30 @@ def include_spec(self) -> pathspec.GitIgnoreSpec | None: @cached_property def exclude_spec(self) -> pathspec.GitIgnoreSpec | None: - if 'exclude' in self.target_config: + if "exclude" in self.target_config: exclude_config = self.target_config - exclude_location = f'tool.hatch.build.targets.{self.plugin_name}.exclude' + exclude_location = f"tool.hatch.build.targets.{self.plugin_name}.exclude" else: exclude_config = self.build_config - exclude_location = 'tool.hatch.build.exclude' + exclude_location = "tool.hatch.build.exclude" all_exclude_patterns = self.default_global_exclude() if not self.ignore_vcs: all_exclude_patterns.extend(self.load_vcs_exclusion_patterns()) - exclude_patterns = exclude_config.get('exclude', self.default_exclude()) + exclude_patterns = exclude_config.get("exclude", self.default_exclude()) if not isinstance(exclude_patterns, list): - message = f'Field `{exclude_location}` must be an array of strings' + message = f"Field `{exclude_location}` must be an array of strings" raise TypeError(message) for i, exclude_pattern in enumerate(exclude_patterns, 1): if not isinstance(exclude_pattern, str): - message = f'Pattern #{i} in field `{exclude_location}` must be a string' + message = f"Pattern #{i} in field `{exclude_location}` must be a string" raise TypeError(message) if not exclude_pattern: - message = f'Pattern #{i} in field `{exclude_location}` cannot be an empty string' + message = f"Pattern #{i} in field `{exclude_location}` cannot be an empty string" raise ValueError(message) all_exclude_patterns.append(exclude_pattern) @@ -185,27 +185,27 @@ def exclude_spec(self) -> pathspec.GitIgnoreSpec | None: @property def artifact_spec(self) -> pathspec.GitIgnoreSpec | None: - if 'artifacts' in self.target_config: + if "artifacts" in self.target_config: artifact_config = self.target_config - artifact_location = f'tool.hatch.build.targets.{self.plugin_name}.artifacts' + artifact_location = f"tool.hatch.build.targets.{self.plugin_name}.artifacts" else: artifact_config = self.build_config - artifact_location = 'tool.hatch.build.artifacts' + artifact_location = "tool.hatch.build.artifacts" all_artifact_patterns = [] - artifact_patterns = artifact_config.get('artifacts', []) + artifact_patterns = artifact_config.get("artifacts", []) if not isinstance(artifact_patterns, list): - message = f'Field `{artifact_location}` must be an array of strings' + message = f"Field `{artifact_location}` must be an array of strings" raise TypeError(message) for i, artifact_pattern in enumerate(artifact_patterns, 1): if not isinstance(artifact_pattern, str): - message = f'Pattern #{i} in field `{artifact_location}` must be a string' + message = f"Pattern #{i} in field `{artifact_location}` must be a string" raise TypeError(message) if not artifact_pattern: - message = f'Pattern #{i} in field `{artifact_location}` cannot be an empty string' + message = f"Pattern #{i} in field `{artifact_location}` cannot be an empty string" raise ValueError(message) all_artifact_patterns.append(artifact_pattern) @@ -218,26 +218,26 @@ def artifact_spec(self) -> pathspec.GitIgnoreSpec | None: def hook_config(self) -> dict[str, Any]: hook_config: dict[str, dict[str, Any]] = {} - global_hook_config = self.build_config.get('hooks', {}) + global_hook_config = self.build_config.get("hooks", {}) if not isinstance(global_hook_config, dict): - message = 'Field `tool.hatch.build.hooks` must be a table' + message = "Field `tool.hatch.build.hooks` must be a table" raise TypeError(message) for hook_name, config in global_hook_config.items(): if not isinstance(config, dict): - message = f'Field `tool.hatch.build.hooks.{hook_name}` must be a table' + message = f"Field `tool.hatch.build.hooks.{hook_name}` must be a table" raise TypeError(message) hook_config.setdefault(hook_name, config) - target_hook_config = self.target_config.get('hooks', {}) + target_hook_config = self.target_config.get("hooks", {}) if not isinstance(target_hook_config, dict): - message = f'Field `tool.hatch.build.targets.{self.plugin_name}.hooks` must be a table' + message = f"Field `tool.hatch.build.targets.{self.plugin_name}.hooks` must be a table" raise TypeError(message) for hook_name, config in target_hook_config.items(): if not isinstance(config, dict): - message = f'Field `tool.hatch.build.targets.{self.plugin_name}.hooks.{hook_name}` must be a table' + message = f"Field `tool.hatch.build.targets.{self.plugin_name}.hooks.{hook_name}` must be a table" raise TypeError(message) hook_config[hook_name] = config @@ -249,8 +249,8 @@ def hook_config(self) -> dict[str, Any]: for hook_name, config in hook_config.items() if ( all_hooks_enabled - or config.get('enable-by-default', True) - or env_var_enabled(f'{BuildEnvVars.HOOK_ENABLE_PREFIX}{hook_name.upper()}') + or config.get("enable-by-default", True) + or env_var_enabled(f"{BuildEnvVars.HOOK_ENABLE_PREFIX}{hook_name.upper()}") ) } else: @@ -260,96 +260,96 @@ def hook_config(self) -> dict[str, Any]: @cached_property def directory(self) -> str: - if 'directory' in self.target_config: - directory = self.target_config['directory'] + if "directory" in self.target_config: + directory = self.target_config["directory"] if not isinstance(directory, str): - message = f'Field `tool.hatch.build.targets.{self.plugin_name}.directory` must be a string' + message = f"Field `tool.hatch.build.targets.{self.plugin_name}.directory` must be a string" raise TypeError(message) else: - directory = self.build_config.get('directory', DEFAULT_BUILD_DIRECTORY) + directory = self.build_config.get("directory", DEFAULT_BUILD_DIRECTORY) if not isinstance(directory, str): - message = 'Field `tool.hatch.build.directory` must be a string' + message = "Field `tool.hatch.build.directory` must be a string" raise TypeError(message) return self.normalize_build_directory(directory) @cached_property def skip_excluded_dirs(self) -> bool: - if 'skip-excluded-dirs' in self.target_config: - skip_excluded_dirs = self.target_config['skip-excluded-dirs'] + if "skip-excluded-dirs" in self.target_config: + skip_excluded_dirs = self.target_config["skip-excluded-dirs"] if not isinstance(skip_excluded_dirs, bool): - message = f'Field `tool.hatch.build.targets.{self.plugin_name}.skip-excluded-dirs` must be a boolean' + message = f"Field `tool.hatch.build.targets.{self.plugin_name}.skip-excluded-dirs` must be a boolean" raise TypeError(message) else: - skip_excluded_dirs = self.build_config.get('skip-excluded-dirs', False) + skip_excluded_dirs = self.build_config.get("skip-excluded-dirs", False) if not isinstance(skip_excluded_dirs, bool): - message = 'Field `tool.hatch.build.skip-excluded-dirs` must be a boolean' + message = "Field `tool.hatch.build.skip-excluded-dirs` must be a boolean" raise TypeError(message) return skip_excluded_dirs @cached_property def ignore_vcs(self) -> bool: - if 'ignore-vcs' in self.target_config: - ignore_vcs = self.target_config['ignore-vcs'] + if "ignore-vcs" in self.target_config: + ignore_vcs = self.target_config["ignore-vcs"] if not isinstance(ignore_vcs, bool): - message = f'Field `tool.hatch.build.targets.{self.plugin_name}.ignore-vcs` must be a boolean' + message = f"Field `tool.hatch.build.targets.{self.plugin_name}.ignore-vcs` must be a boolean" raise TypeError(message) else: - ignore_vcs = self.build_config.get('ignore-vcs', False) + ignore_vcs = self.build_config.get("ignore-vcs", False) if not isinstance(ignore_vcs, bool): - message = 'Field `tool.hatch.build.ignore-vcs` must be a boolean' + message = "Field `tool.hatch.build.ignore-vcs` must be a boolean" raise TypeError(message) return ignore_vcs @cached_property def require_runtime_dependencies(self) -> bool: - if 'require-runtime-dependencies' in self.target_config: - require_runtime_dependencies = self.target_config['require-runtime-dependencies'] + if "require-runtime-dependencies" in self.target_config: + require_runtime_dependencies = self.target_config["require-runtime-dependencies"] if not isinstance(require_runtime_dependencies, bool): message = ( - f'Field `tool.hatch.build.targets.{self.plugin_name}.require-runtime-dependencies` ' - f'must be a boolean' + f"Field `tool.hatch.build.targets.{self.plugin_name}.require-runtime-dependencies` " + f"must be a boolean" ) raise TypeError(message) else: - require_runtime_dependencies = self.build_config.get('require-runtime-dependencies', False) + require_runtime_dependencies = self.build_config.get("require-runtime-dependencies", False) if not isinstance(require_runtime_dependencies, bool): - message = 'Field `tool.hatch.build.require-runtime-dependencies` must be a boolean' + message = "Field `tool.hatch.build.require-runtime-dependencies` must be a boolean" raise TypeError(message) return require_runtime_dependencies @cached_property def require_runtime_features(self) -> list[str]: - if 'require-runtime-features' in self.target_config: + if "require-runtime-features" in self.target_config: features_config = self.target_config - features_location = f'tool.hatch.build.targets.{self.plugin_name}.require-runtime-features' + features_location = f"tool.hatch.build.targets.{self.plugin_name}.require-runtime-features" else: features_config = self.build_config - features_location = 'tool.hatch.build.require-runtime-features' + features_location = "tool.hatch.build.require-runtime-features" - require_runtime_features = features_config.get('require-runtime-features', []) + require_runtime_features = features_config.get("require-runtime-features", []) if not isinstance(require_runtime_features, list): - message = f'Field `{features_location}` must be an array' + message = f"Field `{features_location}` must be an array" raise TypeError(message) all_features: dict[str, None] = {} for i, raw_feature in enumerate(require_runtime_features, 1): if not isinstance(raw_feature, str): - message = f'Feature #{i} of field `{features_location}` must be a string' + message = f"Feature #{i} of field `{features_location}` must be a string" raise TypeError(message) if not raw_feature: - message = f'Feature #{i} of field `{features_location}` cannot be an empty string' + message = f"Feature #{i} of field `{features_location}` cannot be an empty string" raise ValueError(message) feature = normalize_project_name(raw_feature) if feature not in self.builder.metadata.core.optional_dependencies: message = ( - f'Feature `{feature}` of field `{features_location}` is not defined in ' - f'field `project.optional-dependencies`' + f"Feature `{feature}` of field `{features_location}` is not defined in " + f"field `project.optional-dependencies`" ) raise ValueError(message) @@ -362,15 +362,15 @@ def only_packages(self) -> bool: """ Whether or not the target should ignore non-artifact files that do not reside within a Python package. """ - if 'only-packages' in self.target_config: - only_packages = self.target_config['only-packages'] + if "only-packages" in self.target_config: + only_packages = self.target_config["only-packages"] if not isinstance(only_packages, bool): - message = f'Field `tool.hatch.build.targets.{self.plugin_name}.only-packages` must be a boolean' + message = f"Field `tool.hatch.build.targets.{self.plugin_name}.only-packages` must be a boolean" raise TypeError(message) else: - only_packages = self.build_config.get('only-packages', False) + only_packages = self.build_config.get("only-packages", False) if not isinstance(only_packages, bool): - message = 'Field `tool.hatch.build.only-packages` must be a boolean' + message = "Field `tool.hatch.build.only-packages` must be a boolean" raise TypeError(message) return only_packages @@ -380,15 +380,15 @@ def reproducible(self) -> bool: """ Whether or not the target should be built in a reproducible manner, defaulting to true. """ - if 'reproducible' in self.target_config: - reproducible = self.target_config['reproducible'] + if "reproducible" in self.target_config: + reproducible = self.target_config["reproducible"] if not isinstance(reproducible, bool): - message = f'Field `tool.hatch.build.targets.{self.plugin_name}.reproducible` must be a boolean' + message = f"Field `tool.hatch.build.targets.{self.plugin_name}.reproducible` must be a boolean" raise TypeError(message) else: - reproducible = self.build_config.get('reproducible', True) + reproducible = self.build_config.get("reproducible", True) if not isinstance(reproducible, bool): - message = 'Field `tool.hatch.build.reproducible` must be a boolean' + message = "Field `tool.hatch.build.reproducible` must be a boolean" raise TypeError(message) return reproducible @@ -399,27 +399,27 @@ def dev_mode_dirs(self) -> list[str]: Directories which must be added to Python's search path in [dev mode](../config/environment/overview.md#dev-mode). """ - if 'dev-mode-dirs' in self.target_config: + if "dev-mode-dirs" in self.target_config: dev_mode_dirs_config = self.target_config - dev_mode_dirs_location = f'tool.hatch.build.targets.{self.plugin_name}.dev-mode-dirs' + dev_mode_dirs_location = f"tool.hatch.build.targets.{self.plugin_name}.dev-mode-dirs" else: dev_mode_dirs_config = self.build_config - dev_mode_dirs_location = 'tool.hatch.build.dev-mode-dirs' + dev_mode_dirs_location = "tool.hatch.build.dev-mode-dirs" all_dev_mode_dirs = [] - dev_mode_dirs = dev_mode_dirs_config.get('dev-mode-dirs', []) + dev_mode_dirs = dev_mode_dirs_config.get("dev-mode-dirs", []) if not isinstance(dev_mode_dirs, list): - message = f'Field `{dev_mode_dirs_location}` must be an array of strings' + message = f"Field `{dev_mode_dirs_location}` must be an array of strings" raise TypeError(message) for i, dev_mode_dir in enumerate(dev_mode_dirs, 1): if not isinstance(dev_mode_dir, str): - message = f'Directory #{i} in field `{dev_mode_dirs_location}` must be a string' + message = f"Directory #{i} in field `{dev_mode_dirs_location}` must be a string" raise TypeError(message) if not dev_mode_dir: - message = f'Directory #{i} in field `{dev_mode_dirs_location}` cannot be an empty string' + message = f"Directory #{i} in field `{dev_mode_dirs_location}` cannot be an empty string" raise ValueError(message) all_dev_mode_dirs.append(dev_mode_dir) @@ -428,15 +428,15 @@ def dev_mode_dirs(self) -> list[str]: @cached_property def dev_mode_exact(self) -> bool: - if 'dev-mode-exact' in self.target_config: - dev_mode_exact = self.target_config['dev-mode-exact'] + if "dev-mode-exact" in self.target_config: + dev_mode_exact = self.target_config["dev-mode-exact"] if not isinstance(dev_mode_exact, bool): - message = f'Field `tool.hatch.build.targets.{self.plugin_name}.dev-mode-exact` must be a boolean' + message = f"Field `tool.hatch.build.targets.{self.plugin_name}.dev-mode-exact` must be a boolean" raise TypeError(message) else: - dev_mode_exact = self.build_config.get('dev-mode-exact', False) + dev_mode_exact = self.build_config.get("dev-mode-exact", False) if not isinstance(dev_mode_exact, bool): - message = 'Field `tool.hatch.build.dev-mode-exact` must be a boolean' + message = "Field `tool.hatch.build.dev-mode-exact` must be a boolean" raise TypeError(message) return dev_mode_exact @@ -446,22 +446,22 @@ def versions(self) -> list[str]: # Used as an ordered set all_versions: dict[str, None] = {} - versions = self.target_config.get('versions', []) + versions = self.target_config.get("versions", []) if not isinstance(versions, list): - message = f'Field `tool.hatch.build.targets.{self.plugin_name}.versions` must be an array of strings' + message = f"Field `tool.hatch.build.targets.{self.plugin_name}.versions` must be an array of strings" raise TypeError(message) for i, version in enumerate(versions, 1): if not isinstance(version, str): message = ( - f'Version #{i} in field `tool.hatch.build.targets.{self.plugin_name}.versions` must be a string' + f"Version #{i} in field `tool.hatch.build.targets.{self.plugin_name}.versions` must be a string" ) raise TypeError(message) if not version: message = ( - f'Version #{i} in field `tool.hatch.build.targets.{self.plugin_name}.versions` ' - f'cannot be an empty string' + f"Version #{i} in field `tool.hatch.build.targets.{self.plugin_name}.versions` " + f"cannot be an empty string" ) raise ValueError(message) @@ -475,8 +475,8 @@ def versions(self) -> list[str]: unknown_versions = set(all_versions) - set(self.__builder.get_version_api()) if unknown_versions: message = ( - f'Unknown versions in field `tool.hatch.build.targets.{self.plugin_name}.versions`: ' - f'{", ".join(map(str, sorted(unknown_versions)))}' + f"Unknown versions in field `tool.hatch.build.targets.{self.plugin_name}.versions`: " + f"{', '.join(map(str, sorted(unknown_versions)))}" ) raise ValueError(message) @@ -487,29 +487,29 @@ def dependencies(self) -> list[str]: # Used as an ordered set dependencies: dict[str, None] = {} - target_dependencies = self.target_config.get('dependencies', []) + target_dependencies = self.target_config.get("dependencies", []) if not isinstance(target_dependencies, list): - message = f'Field `tool.hatch.build.targets.{self.plugin_name}.dependencies` must be an array' + message = f"Field `tool.hatch.build.targets.{self.plugin_name}.dependencies` must be an array" raise TypeError(message) for i, dependency in enumerate(target_dependencies, 1): if not isinstance(dependency, str): message = ( - f'Dependency #{i} of field `tool.hatch.build.targets.{self.plugin_name}.dependencies` ' - f'must be a string' + f"Dependency #{i} of field `tool.hatch.build.targets.{self.plugin_name}.dependencies` " + f"must be a string" ) raise TypeError(message) dependencies[dependency] = None - global_dependencies = self.build_config.get('dependencies', []) + global_dependencies = self.build_config.get("dependencies", []) if not isinstance(global_dependencies, list): - message = 'Field `tool.hatch.build.dependencies` must be an array' + message = "Field `tool.hatch.build.dependencies` must be an array" raise TypeError(message) for i, dependency in enumerate(global_dependencies, 1): if not isinstance(dependency, str): - message = f'Dependency #{i} of field `tool.hatch.build.dependencies` must be a string' + message = f"Dependency #{i} of field `tool.hatch.build.dependencies` must be a string" raise TypeError(message) dependencies[dependency] = None @@ -517,52 +517,52 @@ def dependencies(self) -> list[str]: require_runtime_dependencies = self.require_runtime_dependencies require_runtime_features = dict.fromkeys(self.require_runtime_features) for hook_name, config in self.hook_config.items(): - hook_require_runtime_dependencies = config.get('require-runtime-dependencies', False) + hook_require_runtime_dependencies = config.get("require-runtime-dependencies", False) if not isinstance(hook_require_runtime_dependencies, bool): - message = f'Option `require-runtime-dependencies` of build hook `{hook_name}` must be a boolean' + message = f"Option `require-runtime-dependencies` of build hook `{hook_name}` must be a boolean" raise TypeError(message) if hook_require_runtime_dependencies: require_runtime_dependencies = True - hook_require_runtime_features = config.get('require-runtime-features', []) + hook_require_runtime_features = config.get("require-runtime-features", []) if not isinstance(hook_require_runtime_features, list): - message = f'Option `require-runtime-features` of build hook `{hook_name}` must be an array' + message = f"Option `require-runtime-features` of build hook `{hook_name}` must be an array" raise TypeError(message) for i, raw_feature in enumerate(hook_require_runtime_features, 1): if not isinstance(raw_feature, str): message = ( - f'Feature #{i} of option `require-runtime-features` of build hook `{hook_name}` ' - f'must be a string' + f"Feature #{i} of option `require-runtime-features` of build hook `{hook_name}` " + f"must be a string" ) raise TypeError(message) if not raw_feature: message = ( - f'Feature #{i} of option `require-runtime-features` of build hook `{hook_name}` ' - f'cannot be an empty string' + f"Feature #{i} of option `require-runtime-features` of build hook `{hook_name}` " + f"cannot be an empty string" ) raise ValueError(message) feature = normalize_project_name(raw_feature) if feature not in self.builder.metadata.core.optional_dependencies: message = ( - f'Feature `{feature}` of option `require-runtime-features` of build hook `{hook_name}` ' - f'is not defined in field `project.optional-dependencies`' + f"Feature `{feature}` of option `require-runtime-features` of build hook `{hook_name}` " + f"is not defined in field `project.optional-dependencies`" ) raise ValueError(message) require_runtime_features[feature] = None - hook_dependencies = config.get('dependencies', []) + hook_dependencies = config.get("dependencies", []) if not isinstance(hook_dependencies, list): - message = f'Option `dependencies` of build hook `{hook_name}` must be an array' + message = f"Option `dependencies` of build hook `{hook_name}` must be an array" raise TypeError(message) for i, dependency in enumerate(hook_dependencies, 1): if not isinstance(dependency, str): - message = f'Dependency #{i} of option `dependencies` of build hook `{hook_name}` must be a string' + message = f"Dependency #{i} of option `dependencies` of build hook `{hook_name}` must be a string" raise TypeError(message) dependencies[dependency] = None @@ -593,7 +593,7 @@ def dynamic_dependencies(self) -> list[str]: # This happens for example when using the `custom` build hook. try: build_hook = build_hook_cls( - self.root, config, self, self.builder.metadata, '', self.builder.PLUGIN_NAME, self.builder.app + self.root, config, self, self.builder.metadata, "", self.builder.PLUGIN_NAME, self.builder.app ) except ImportError: continue @@ -604,133 +604,133 @@ def dynamic_dependencies(self) -> list[str]: @cached_property def sources(self) -> dict[str, str]: - if 'sources' in self.target_config: + if "sources" in self.target_config: sources_config = self.target_config - sources_location = f'tool.hatch.build.targets.{self.plugin_name}.sources' + sources_location = f"tool.hatch.build.targets.{self.plugin_name}.sources" else: sources_config = self.build_config - sources_location = 'tool.hatch.build.sources' + sources_location = "tool.hatch.build.sources" sources = {} - raw_sources = sources_config.get('sources', []) + raw_sources = sources_config.get("sources", []) if isinstance(raw_sources, list): for i, source in enumerate(raw_sources, 1): if not isinstance(source, str): - message = f'Source #{i} in field `{sources_location}` must be a string' + message = f"Source #{i} in field `{sources_location}` must be a string" raise TypeError(message) if not source: - message = f'Source #{i} in field `{sources_location}` cannot be an empty string' + message = f"Source #{i} in field `{sources_location}` cannot be an empty string" raise ValueError(message) - sources[normalize_relative_directory(source)] = '' + sources[normalize_relative_directory(source)] = "" elif isinstance(raw_sources, dict): for source, path in raw_sources.items(): if not isinstance(path, str): - message = f'Path for source `{source}` in field `{sources_location}` must be a string' + message = f"Path for source `{source}` in field `{sources_location}` must be a string" raise TypeError(message) normalized_path = normalize_relative_path(path) - if normalized_path == '.': - normalized_path = '' + if normalized_path == ".": + normalized_path = "" else: normalized_path += os.sep sources[normalize_relative_directory(source) if source else source] = normalized_path else: - message = f'Field `{sources_location}` must be a mapping or array of strings' + message = f"Field `{sources_location}` must be a mapping or array of strings" raise TypeError(message) for relative_path in self.packages: source, _package = os.path.split(relative_path) if source and normalize_relative_directory(relative_path) not in sources: - sources[normalize_relative_directory(source)] = '' + sources[normalize_relative_directory(source)] = "" return dict(sorted(sources.items())) @cached_property def packages(self) -> list[str]: - if 'packages' in self.target_config: + if "packages" in self.target_config: package_config = self.target_config - package_location = f'tool.hatch.build.targets.{self.plugin_name}.packages' + package_location = f"tool.hatch.build.targets.{self.plugin_name}.packages" else: package_config = self.build_config - package_location = 'tool.hatch.build.packages' + package_location = "tool.hatch.build.packages" - packages = package_config.get('packages', self.default_packages()) + packages = package_config.get("packages", self.default_packages()) if not isinstance(packages, list): - message = f'Field `{package_location}` must be an array of strings' + message = f"Field `{package_location}` must be an array of strings" raise TypeError(message) for i, package in enumerate(packages, 1): if not isinstance(package, str): - message = f'Package #{i} in field `{package_location}` must be a string' + message = f"Package #{i} in field `{package_location}` must be a string" raise TypeError(message) if not package: - message = f'Package #{i} in field `{package_location}` cannot be an empty string' + message = f"Package #{i} in field `{package_location}` cannot be an empty string" raise ValueError(message) return sorted(normalize_relative_path(package) for package in packages) @cached_property def force_include(self) -> dict[str, str]: - if 'force-include' in self.target_config: + if "force-include" in self.target_config: force_include_config = self.target_config - force_include_location = f'tool.hatch.build.targets.{self.plugin_name}.force-include' + force_include_location = f"tool.hatch.build.targets.{self.plugin_name}.force-include" else: force_include_config = self.build_config - force_include_location = 'tool.hatch.build.force-include' + force_include_location = "tool.hatch.build.force-include" - force_include = force_include_config.get('force-include', {}) + force_include = force_include_config.get("force-include", {}) if not isinstance(force_include, dict): - message = f'Field `{force_include_location}` must be a mapping' + message = f"Field `{force_include_location}` must be a mapping" raise TypeError(message) for i, (source, relative_path) in enumerate(force_include.items(), 1): if not source: - message = f'Source #{i} in field `{force_include_location}` cannot be an empty string' + message = f"Source #{i} in field `{force_include_location}` cannot be an empty string" raise ValueError(message) if not isinstance(relative_path, str): - message = f'Path for source `{source}` in field `{force_include_location}` must be a string' + message = f"Path for source `{source}` in field `{force_include_location}` must be a string" raise TypeError(message) if not relative_path: - message = f'Path for source `{source}` in field `{force_include_location}` cannot be an empty string' + message = f"Path for source `{source}` in field `{force_include_location}` cannot be an empty string" raise ValueError(message) return normalize_inclusion_map(force_include, self.root) @cached_property def only_include(self) -> dict[str, str]: - if 'only-include' in self.target_config: + if "only-include" in self.target_config: only_include_config = self.target_config - only_include_location = f'tool.hatch.build.targets.{self.plugin_name}.only-include' + only_include_location = f"tool.hatch.build.targets.{self.plugin_name}.only-include" else: only_include_config = self.build_config - only_include_location = 'tool.hatch.build.only-include' + only_include_location = "tool.hatch.build.only-include" - only_include = only_include_config.get('only-include', self.default_only_include()) or self.packages + only_include = only_include_config.get("only-include", self.default_only_include()) or self.packages if not isinstance(only_include, list): - message = f'Field `{only_include_location}` must be an array' + message = f"Field `{only_include_location}` must be an array" raise TypeError(message) inclusion_map = {} for i, relative_path in enumerate(only_include, 1): if not isinstance(relative_path, str): - message = f'Path #{i} in field `{only_include_location}` must be a string' + message = f"Path #{i} in field `{only_include_location}` must be a string" raise TypeError(message) normalized_path = normalize_relative_path(relative_path) - if not normalized_path or normalized_path.startswith(('~', '..')): - message = f'Path #{i} in field `{only_include_location}` must be relative: {relative_path}' + if not normalized_path or normalized_path.startswith(("~", "..")): + message = f"Path #{i} in field `{only_include_location}` must be relative: {relative_path}" raise ValueError(message) if normalized_path in inclusion_map: - message = f'Duplicate path in field `{only_include_location}`: {normalized_path}' + message = f"Duplicate path in field `{only_include_location}`: {normalized_path}" raise ValueError(message) inclusion_map[normalized_path] = normalized_path @@ -750,15 +750,15 @@ def get_distribution_path(self, relative_path: str) -> str: @cached_property def vcs_exclusion_files(self) -> dict[str, list[str]]: - exclusion_files: dict[str, list[str]] = {'git': [], 'hg': []} + exclusion_files: dict[str, list[str]] = {"git": [], "hg": []} - local_gitignore = locate_file(self.root, '.gitignore', boundary='.git') + local_gitignore = locate_file(self.root, ".gitignore", boundary=".git") if local_gitignore is not None: - exclusion_files['git'].append(local_gitignore) + exclusion_files["git"].append(local_gitignore) - local_hgignore = locate_file(self.root, '.hgignore', boundary='.hg') + local_hgignore = locate_file(self.root, ".hgignore", boundary=".hg") if local_hgignore is not None: - exclusion_files['hg'].append(local_hgignore) + exclusion_files["hg"].append(local_hgignore) return exclusion_files @@ -766,21 +766,21 @@ def load_vcs_exclusion_patterns(self) -> list[str]: patterns = [] # https://git-scm.com/docs/gitignore#_pattern_format - for exclusion_file in self.vcs_exclusion_files['git']: - with open(exclusion_file, encoding='utf-8') as f: + for exclusion_file in self.vcs_exclusion_files["git"]: + with open(exclusion_file, encoding="utf-8") as f: patterns.extend(f.readlines()) # https://linux.die.net/man/5/hgignore - for exclusion_file in self.vcs_exclusion_files['hg']: - with open(exclusion_file, encoding='utf-8') as f: + for exclusion_file in self.vcs_exclusion_files["hg"]: + with open(exclusion_file, encoding="utf-8") as f: glob_mode = False for line in f: exact_line = line.strip() - if exact_line == 'syntax: glob': + if exact_line == "syntax: glob": glob_mode = True continue - if exact_line.startswith('syntax: '): + if exact_line.startswith("syntax: "): glob_mode = False continue @@ -813,7 +813,7 @@ def default_only_include(self) -> list: # noqa: PLR6301 return [] def default_global_exclude(self) -> list[str]: # noqa: PLR6301 - patterns = ['*.py[cdo]', f'/{DEFAULT_BUILD_DIRECTORY}'] + patterns = ["*.py[cdo]", f"/{DEFAULT_BUILD_DIRECTORY}"] patterns.sort() return patterns @@ -829,18 +829,18 @@ def get_force_include(self) -> dict[str, str]: def set_build_data(self, build_data: dict[str, Any]) -> Generator: try: # Include anything the hooks indicate - build_artifacts = build_data['artifacts'] + build_artifacts = build_data["artifacts"] if build_artifacts: self.build_artifact_spec = pathspec.GitIgnoreSpec.from_lines(build_artifacts) - self.build_force_include.update(normalize_inclusion_map(build_data['force_include'], self.root)) + self.build_force_include.update(normalize_inclusion_map(build_data["force_include"], self.root)) for inclusion_map in (self.force_include, self.build_force_include): for source, target in inclusion_map.items(): # Ignore source # old/ -> new/ # old.ext -> new.ext - if source.startswith(f'{self.root}{os.sep}'): + if source.startswith(f"{self.root}{os.sep}"): self.build_reserved_paths.add(self.get_distribution_path(os.path.relpath(source, self.root))) # Ignore target files only # ../out.ext -> ../in.ext @@ -856,9 +856,9 @@ def set_build_data(self, build_data: dict[str, Any]) -> Generator: def env_var_enabled(env_var: str, *, default: bool = False) -> bool: if env_var in os.environ: - return os.environ[env_var] in {'1', 'true'} + return os.environ[env_var] in {"1", "true"} return default -BuilderConfigBound = TypeVar('BuilderConfigBound', bound=BuilderConfig) +BuilderConfigBound = TypeVar("BuilderConfigBound", bound=BuilderConfig) diff --git a/backend/src/hatchling/builders/constants.py b/backend/src/hatchling/builders/constants.py index ac718da73..fe5b0b581 100644 --- a/backend/src/hatchling/builders/constants.py +++ b/backend/src/hatchling/builders/constants.py @@ -1,43 +1,43 @@ -DEFAULT_BUILD_DIRECTORY = 'dist' +DEFAULT_BUILD_DIRECTORY = "dist" EXCLUDED_DIRECTORIES = frozenset(( # Python bytecode - '__pycache__', + "__pycache__", # Single virtual environment - '.venv', + ".venv", # Git - '.git', + ".git", # Mercurial - '.hg', + ".hg", # Hatch - '.hatch', + ".hatch", # tox - '.tox', + ".tox", # nox - '.nox', + ".nox", # Ruff - '.ruff_cache', + ".ruff_cache", # pytest - '.pytest_cache', + ".pytest_cache", # Mypy - '.mypy_cache', + ".mypy_cache", # pixi - '.pixi', + ".pixi", )) EXCLUDED_FILES = frozenset(( # https://en.wikipedia.org/wiki/.DS_Store - '.DS_Store', + ".DS_Store", )) class BuildEnvVars: - LOCATION = 'HATCH_BUILD_LOCATION' - HOOKS_ONLY = 'HATCH_BUILD_HOOKS_ONLY' - NO_HOOKS = 'HATCH_BUILD_NO_HOOKS' - HOOKS_ENABLE = 'HATCH_BUILD_HOOKS_ENABLE' - HOOK_ENABLE_PREFIX = 'HATCH_BUILD_HOOK_ENABLE_' - CLEAN = 'HATCH_BUILD_CLEAN' - CLEAN_HOOKS_AFTER = 'HATCH_BUILD_CLEAN_HOOKS_AFTER' + LOCATION = "HATCH_BUILD_LOCATION" + HOOKS_ONLY = "HATCH_BUILD_HOOKS_ONLY" + NO_HOOKS = "HATCH_BUILD_NO_HOOKS" + HOOKS_ENABLE = "HATCH_BUILD_HOOKS_ENABLE" + HOOK_ENABLE_PREFIX = "HATCH_BUILD_HOOK_ENABLE_" + CLEAN = "HATCH_BUILD_CLEAN" + CLEAN_HOOKS_AFTER = "HATCH_BUILD_CLEAN_HOOKS_AFTER" -EDITABLES_REQUIREMENT = 'editables~=0.3' +EDITABLES_REQUIREMENT = "editables~=0.3" diff --git a/backend/src/hatchling/builders/custom.py b/backend/src/hatchling/builders/custom.py index 88fa5b004..dc25b23c7 100644 --- a/backend/src/hatchling/builders/custom.py +++ b/backend/src/hatchling/builders/custom.py @@ -14,7 +14,7 @@ class CustomBuilder(Generic[PluginManagerBound]): - PLUGIN_NAME = 'custom' + PLUGIN_NAME = "custom" def __new__( # type: ignore[misc] cls, @@ -28,24 +28,24 @@ def __new__( # type: ignore[misc] target_config = project_metadata.hatch.build_targets.get(cls.PLUGIN_NAME, {}) if not isinstance(target_config, dict): - message = f'Field `tool.hatch.build.targets.{cls.PLUGIN_NAME}` must be a table' + message = f"Field `tool.hatch.build.targets.{cls.PLUGIN_NAME}` must be a table" raise TypeError(message) - build_script = target_config.get('path', DEFAULT_BUILD_SCRIPT) + build_script = target_config.get("path", DEFAULT_BUILD_SCRIPT) if not isinstance(build_script, str): - message = f'Option `path` for builder `{cls.PLUGIN_NAME}` must be a string' + message = f"Option `path` for builder `{cls.PLUGIN_NAME}` must be a string" raise TypeError(message) if not build_script: - message = f'Option `path` for builder `{cls.PLUGIN_NAME}` must not be empty if defined' + message = f"Option `path` for builder `{cls.PLUGIN_NAME}` must not be empty if defined" raise ValueError(message) path = os.path.normpath(os.path.join(root, build_script)) if not os.path.isfile(path): - message = f'Build script does not exist: {build_script}' + message = f"Build script does not exist: {build_script}" raise OSError(message) - hook_class = load_plugin_from_script(path, build_script, BuilderInterface, 'builder') # type: ignore[type-abstract] + hook_class = load_plugin_from_script(path, build_script, BuilderInterface, "builder") # type: ignore[type-abstract] hook = hook_class(root, plugin_manager=plugin_manager, config=config, metadata=metadata, app=app) # Always keep the name to avoid confusion diff --git a/backend/src/hatchling/builders/hooks/custom.py b/backend/src/hatchling/builders/hooks/custom.py index a1f75a886..a561f993f 100644 --- a/backend/src/hatchling/builders/hooks/custom.py +++ b/backend/src/hatchling/builders/hooks/custom.py @@ -9,7 +9,7 @@ class CustomBuildHook: - PLUGIN_NAME = 'custom' + PLUGIN_NAME = "custom" def __new__( # type: ignore[misc] cls, @@ -18,21 +18,21 @@ def __new__( # type: ignore[misc] *args: Any, **kwargs: Any, ) -> BuildHookInterface: - build_script = config.get('path', DEFAULT_BUILD_SCRIPT) + build_script = config.get("path", DEFAULT_BUILD_SCRIPT) if not isinstance(build_script, str): - message = f'Option `path` for build hook `{cls.PLUGIN_NAME}` must be a string' + message = f"Option `path` for build hook `{cls.PLUGIN_NAME}` must be a string" raise TypeError(message) if not build_script: - message = f'Option `path` for build hook `{cls.PLUGIN_NAME}` must not be empty if defined' + message = f"Option `path` for build hook `{cls.PLUGIN_NAME}` must not be empty if defined" raise ValueError(message) path = os.path.normpath(os.path.join(root, build_script)) if not os.path.isfile(path): - message = f'Build script does not exist: {build_script}' + message = f"Build script does not exist: {build_script}" raise OSError(message) - hook_class = load_plugin_from_script(path, build_script, BuildHookInterface, 'build_hook') + hook_class = load_plugin_from_script(path, build_script, BuildHookInterface, "build_hook") hook = hook_class(root, config, *args, **kwargs) # Always keep the name to avoid confusion diff --git a/backend/src/hatchling/builders/hooks/plugin/interface.py b/backend/src/hatchling/builders/hooks/plugin/interface.py index f13cd4379..bc0293fe1 100644 --- a/backend/src/hatchling/builders/hooks/plugin/interface.py +++ b/backend/src/hatchling/builders/hooks/plugin/interface.py @@ -18,7 +18,7 @@ class BuildHookInterface(Generic[BuilderConfigBound]): # no cov class SpecialBuildHook(BuildHookInterface): - PLUGIN_NAME = 'special' + PLUGIN_NAME = "special" ... ``` @@ -34,7 +34,7 @@ def hatch_register_build_hook(): ``` """ - PLUGIN_NAME = '' + PLUGIN_NAME = "" """The name used for selection.""" def __init__( diff --git a/backend/src/hatchling/builders/hooks/version.py b/backend/src/hatchling/builders/hooks/version.py index 334c4c069..01cc65100 100644 --- a/backend/src/hatchling/builders/hooks/version.py +++ b/backend/src/hatchling/builders/hooks/version.py @@ -7,7 +7,7 @@ class VersionBuildHook(BuildHookInterface): - PLUGIN_NAME = 'version' + PLUGIN_NAME = "version" def __init__(self, *args: Any, **kwargs: Any) -> None: super().__init__(*args, **kwargs) @@ -19,13 +19,13 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: @property def config_path(self) -> str: if self.__config_path is None: - path = self.config.get('path', '') + path = self.config.get("path", "") if not isinstance(path, str): - message = f'Option `path` for build hook `{self.PLUGIN_NAME}` must be a string' + message = f"Option `path` for build hook `{self.PLUGIN_NAME}` must be a string" raise TypeError(message) if not path: - message = f'Option `path` for build hook `{self.PLUGIN_NAME}` is required' + message = f"Option `path` for build hook `{self.PLUGIN_NAME}` is required" raise ValueError(message) self.__config_path = path @@ -35,9 +35,9 @@ def config_path(self) -> str: @property def config_template(self) -> str: if self.__config_template is None: - template = self.config.get('template', '') + template = self.config.get("template", "") if not isinstance(template, str): - message = f'Option `template` for build hook `{self.PLUGIN_NAME}` must be a string' + message = f"Option `template` for build hook `{self.PLUGIN_NAME}` must be a string" raise TypeError(message) self.__config_template = template @@ -47,9 +47,9 @@ def config_template(self) -> str: @property def config_pattern(self) -> str | bool: if self.__config_pattern is None: - pattern = self.config.get('pattern', '') + pattern = self.config.get("pattern", "") if not isinstance(pattern, (str, bool)): - message = f'Option `pattern` for build hook `{self.PLUGIN_NAME}` must be a string or a boolean' + message = f"Option `pattern` for build hook `{self.PLUGIN_NAME}` must be a string or a boolean" raise TypeError(message) self.__config_pattern = pattern @@ -68,4 +68,4 @@ def initialize( else: version_file.write(self.metadata.version, self.config_template) - build_data['artifacts'].append(f'/{self.config_path}') + build_data["artifacts"].append(f"/{self.config_path}") diff --git a/backend/src/hatchling/builders/macos.py b/backend/src/hatchling/builders/macos.py index 6857e3a20..ecda7230c 100644 --- a/backend/src/hatchling/builders/macos.py +++ b/backend/src/hatchling/builders/macos.py @@ -4,7 +4,7 @@ import platform import re -__all__ = ['process_macos_plat_tag'] +__all__ = ["process_macos_plat_tag"] def process_macos_plat_tag(plat: str, /, *, compat: bool) -> str: @@ -16,19 +16,19 @@ def process_macos_plat_tag(plat: str, /, *, compat: bool) -> str: """ # Default to a native build current_arch = platform.machine() - arm = current_arch == 'arm64' + arm = current_arch == "arm64" # Look for cross-compiles - archflags = os.environ.get('ARCHFLAGS', '') - if archflags and (archs := re.findall(r'-arch (\S+)', archflags)): - new_arch = 'universal2' if set(archs) == {'x86_64', 'arm64'} else archs[0] - arm = archs == ['arm64'] - plat = f'{plat[: plat.rfind(current_arch)]}{new_arch}' + archflags = os.environ.get("ARCHFLAGS", "") + if archflags and (archs := re.findall(r"-arch (\S+)", archflags)): + new_arch = "universal2" if set(archs) == {"x86_64", "arm64"} else archs[0] + arm = archs == ["arm64"] + plat = f"{plat[: plat.rfind(current_arch)]}{new_arch}" # Process macOS version - if sdk_match := re.search(r'macosx_(\d+_\d+)', plat): + if sdk_match := re.search(r"macosx_(\d+_\d+)", plat): macos_version = sdk_match.group(1) - target = os.environ.get('MACOSX_DEPLOYMENT_TARGET', None) + target = os.environ.get("MACOSX_DEPLOYMENT_TARGET", None) try: new_version = normalize_macos_version(target or macos_version, arm=arm, compat=compat) @@ -46,13 +46,13 @@ def normalize_macos_version(version: str, *, arm: bool, compat: bool) -> str: converted to 10.16 if compat=True. Version is always returned in "major_minor" format. """ - version = version.replace('.', '_') - if '_' not in version: - version = f'{version}_0' - major, minor = (int(d) for d in version.split('_')[:2]) + version = version.replace(".", "_") + if "_" not in version: + version = f"{version}_0" + major, minor = (int(d) for d in version.split("_")[:2]) major = max(major, 11) if arm else major minor = 0 if major >= 11 else minor # noqa: PLR2004 if compat and major >= 11: # noqa: PLR2004 major = 10 minor = 16 - return f'{major}_{minor}' + return f"{major}_{minor}" diff --git a/backend/src/hatchling/builders/plugin/interface.py b/backend/src/hatchling/builders/plugin/interface.py index a660c330c..9996fa293 100644 --- a/backend/src/hatchling/builders/plugin/interface.py +++ b/backend/src/hatchling/builders/plugin/interface.py @@ -19,7 +19,7 @@ class IncludedFile: - __slots__ = ('distribution_path', 'path', 'relative_path') + __slots__ = ("distribution_path", "path", "relative_path") def __init__(self, path: str, relative_path: str, distribution_path: str) -> None: self.path = path @@ -36,7 +36,7 @@ class BuilderInterface(ABC, Generic[BuilderConfigBound, PluginManagerBound]): class SpecialBuilder(BuilderInterface): - PLUGIN_NAME = 'special' + PLUGIN_NAME = "special" ... ``` @@ -52,7 +52,7 @@ def hatch_register_builder(): ``` """ - PLUGIN_NAME = '' + PLUGIN_NAME = "" """The name used for selection.""" def __init__( @@ -108,7 +108,7 @@ def build( unknown_versions = set(versions) - set(version_api) if unknown_versions: message = ( - f'Unknown versions for target `{self.PLUGIN_NAME}`: {", ".join(map(str, sorted(unknown_versions)))}' + f"Unknown versions for target `{self.PLUGIN_NAME}`: {', '.join(map(str, sorted(unknown_versions)))}" ) raise ValueError(message) @@ -136,20 +136,20 @@ def build( clean_hooks_after = env_var_enabled(BuildEnvVars.CLEAN_HOOKS_AFTER) for version in versions: - self.app.display_debug(f'Building `{self.PLUGIN_NAME}` version `{version}`') + self.app.display_debug(f"Building `{self.PLUGIN_NAME}` version `{version}`") build_data = self.get_default_build_data() self.set_build_data_defaults(build_data) # Allow inspection of configured build hooks and the order in which they run - build_data['build_hooks'] = tuple(configured_build_hooks) + build_data["build_hooks"] = tuple(configured_build_hooks) # Execute all `initialize` build hooks for build_hook in build_hooks: build_hook.initialize(version, build_data) if hooks_only: - self.app.display_debug(f'Only ran build hooks for `{self.PLUGIN_NAME}` version `{version}`') + self.app.display_debug(f"Only ran build hooks for `{self.PLUGIN_NAME}` version `{version}`") continue # Build the artifact @@ -191,7 +191,7 @@ def recurse_project_files(self) -> Iterable[IncludedFile]: dirs[:] = sorted(d for d in dirs if not self.config.directory_is_excluded(d, relative_path)) files.sort() - is_package = '__init__.py' in files + is_package = "__init__.py" in files for f in files: if f in EXCLUDED_FILES: continue @@ -212,7 +212,7 @@ def recurse_forced_files(self, inclusion_map: dict[str, str]) -> Iterable[Includ if os.path.isfile(source): yield IncludedFile( source, - '' if external else os.path.relpath(source, self.root), + "" if external else os.path.relpath(source, self.root), self.config.get_distribution_path(target_path), ) elif os.path.isdir(source): @@ -231,11 +231,11 @@ def recurse_forced_files(self, inclusion_map: dict[str, str]) -> Iterable[Includ if not self.config.path_is_reserved(distribution_path): yield IncludedFile( os.path.join(root, f), - '' if external else relative_file_path, + "" if external else relative_file_path, distribution_path, ) else: - msg = f'Forced include not found: {source}' + msg = f"Forced include not found: {source}" raise FileNotFoundError(msg) def recurse_explicit_files(self, inclusion_map: dict[str, str]) -> Iterable[IncludedFile]: @@ -246,7 +246,7 @@ def recurse_explicit_files(self, inclusion_map: dict[str, str]) -> Iterable[Incl if not self.config.path_is_reserved(distribution_path): yield IncludedFile( source, - '' if external else os.path.relpath(source, self.root), + "" if external else os.path.relpath(source, self.root), self.config.get_distribution_path(target_path), ) elif os.path.isdir(source): @@ -256,7 +256,7 @@ def recurse_explicit_files(self, inclusion_map: dict[str, str]) -> Iterable[Incl dirs[:] = sorted(d for d in dirs if d not in EXCLUDED_DIRECTORIES) files.sort() - is_package = '__init__.py' in files + is_package = "__init__.py" in files for f in files: if f in EXCLUDED_FILES: continue @@ -268,7 +268,7 @@ def recurse_explicit_files(self, inclusion_map: dict[str, str]) -> Iterable[Incl if self.config.include_path(relative_file_path, explicit=True, is_package=is_package): yield IncludedFile( - os.path.join(root, f), '' if external else relative_file_path, distribution_path + os.path.join(root, f), "" if external else relative_file_path, distribution_path ) @property @@ -363,7 +363,7 @@ def target_config(self) -> dict[str, Any]: if self.__target_config is None: target_config: dict[str, Any] = self.metadata.hatch.build_targets.get(self.PLUGIN_NAME, {}) if not isinstance(target_config, dict): - message = f'Field `tool.hatch.build.targets.{self.PLUGIN_NAME}` must be a table' + message = f"Field `tool.hatch.build.targets.{self.PLUGIN_NAME}` must be a table" raise TypeError(message) self.__target_config = target_config @@ -373,7 +373,7 @@ def target_config(self) -> dict[str, Any]: @property def project_id(self) -> str: if self.__project_id is None: - self.__project_id = f'{self.normalize_file_name_component(self.metadata.core.name)}-{self.metadata.version}' + self.__project_id = f"{self.normalize_file_name_component(self.metadata.core.name)}-{self.metadata.version}" return self.__project_id @@ -384,7 +384,7 @@ def get_build_hooks(self, directory: str) -> dict[str, BuildHookInterface]: if build_hook is None: from hatchling.plugin.exceptions import UnknownPluginError - message = f'Unknown build hook: {hook_name}' + message = f"Unknown build hook: {hook_name}" raise UnknownPluginError(message) configured_build_hooks[hook_name] = build_hook( @@ -419,8 +419,8 @@ def get_default_build_data(self) -> dict[str, Any]: # noqa: PLR6301 return {} def set_build_data_defaults(self, build_data: dict[str, Any]) -> None: # noqa: PLR6301 - build_data.setdefault('artifacts', []) - build_data.setdefault('force_include', {}) + build_data.setdefault("artifacts", []) + build_data.setdefault("force_include", {}) def clean(self, directory: str, versions: list[str]) -> None: """ @@ -440,4 +440,4 @@ def normalize_file_name_component(file_name: str) -> str: """ https://peps.python.org/pep-0427/#escaping-and-unicode """ - return re.sub(r'[^\w\d.]+', '_', file_name, flags=re.UNICODE) + return re.sub(r"[^\w\d.]+", "_", file_name, flags=re.UNICODE) diff --git a/backend/src/hatchling/builders/sdist.py b/backend/src/hatchling/builders/sdist.py index 45ceedb54..aa27251ef 100644 --- a/backend/src/hatchling/builders/sdist.py +++ b/backend/src/hatchling/builders/sdist.py @@ -36,15 +36,15 @@ def __init__(self, name: str, *, reproducible: bool) -> None: self.reproducible = reproducible self.timestamp: int | None = get_reproducible_timestamp() if reproducible else None - raw_fd, self.path = tempfile.mkstemp(suffix='.tar.gz') - self.fd = os.fdopen(raw_fd, 'w+b') - self.gz = gzip.GzipFile(fileobj=self.fd, mode='wb', mtime=self.timestamp) - self.tf = tarfile.TarFile(fileobj=self.gz, mode='w', format=tarfile.PAX_FORMAT) + raw_fd, self.path = tempfile.mkstemp(suffix=".tar.gz") + self.fd = os.fdopen(raw_fd, "w+b") + self.gz = gzip.GzipFile(fileobj=self.fd, mode="wb", mtime=self.timestamp) + self.tf = tarfile.TarFile(fileobj=self.gz, mode="w", format=tarfile.PAX_FORMAT) self.gettarinfo = lambda *args, **kwargs: self.normalize_tar_metadata(self.tf.gettarinfo(*args, **kwargs)) def create_file(self, contents: str | bytes, *relative_paths: str) -> None: if not isinstance(contents, bytes): - contents = contents.encode('utf-8') + contents = contents.encode("utf-8") tar_info = tarfile.TarInfo(normalize_archive_path(os.path.join(self.name, *relative_paths))) tar_info.size = len(contents) if self.reproducible and self.timestamp is not None: @@ -62,8 +62,8 @@ def normalize_tar_metadata(self, tar_info: tarfile.TarInfo | None) -> tarfile.Ta tar_info = copy(tar_info) tar_info.uid = 0 tar_info.gid = 0 - tar_info.uname = '' - tar_info.gname = '' + tar_info.uname = "" + tar_info.gname = "" tar_info.mode = normalize_file_permissions(tar_info.mode) if self.timestamp is not None: tar_info.mtime = self.timestamp @@ -97,17 +97,17 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: @property def core_metadata_constructor(self) -> Callable[..., str]: if self.__core_metadata_constructor is None: - core_metadata_version = self.target_config.get('core-metadata-version', DEFAULT_METADATA_VERSION) + core_metadata_version = self.target_config.get("core-metadata-version", DEFAULT_METADATA_VERSION) if not isinstance(core_metadata_version, str): - message = f'Field `tool.hatch.build.targets.{self.plugin_name}.core-metadata-version` must be a string' + message = f"Field `tool.hatch.build.targets.{self.plugin_name}.core-metadata-version` must be a string" raise TypeError(message) constructors = get_core_metadata_constructors() if core_metadata_version not in constructors: message = ( - f'Unknown metadata version `{core_metadata_version}` for field ' - f'`tool.hatch.build.targets.{self.plugin_name}.core-metadata-version`. ' - f'Available: {", ".join(sorted(constructors))}' + f"Unknown metadata version `{core_metadata_version}` for field " + f"`tool.hatch.build.targets.{self.plugin_name}.core-metadata-version`. " + f"Available: {', '.join(sorted(constructors))}" ) raise ValueError(message) @@ -118,15 +118,15 @@ def core_metadata_constructor(self) -> Callable[..., str]: @property def strict_naming(self) -> bool: if self.__strict_naming is None: - if 'strict-naming' in self.target_config: - strict_naming = self.target_config['strict-naming'] + if "strict-naming" in self.target_config: + strict_naming = self.target_config["strict-naming"] if not isinstance(strict_naming, bool): - message = f'Field `tool.hatch.build.targets.{self.plugin_name}.strict-naming` must be a boolean' + message = f"Field `tool.hatch.build.targets.{self.plugin_name}.strict-naming` must be a boolean" raise TypeError(message) else: - strict_naming = self.build_config.get('strict-naming', True) + strict_naming = self.build_config.get("strict-naming", True) if not isinstance(strict_naming, bool): - message = 'Field `tool.hatch.build.strict-naming` must be a boolean' + message = "Field `tool.hatch.build.strict-naming` must be a boolean" raise TypeError(message) self.__strict_naming = strict_naming @@ -136,7 +136,7 @@ def strict_naming(self) -> bool: @property def support_legacy(self) -> bool: if self.__support_legacy is None: - self.__support_legacy = bool(self.target_config.get('support-legacy', False)) + self.__support_legacy = bool(self.target_config.get("support-legacy", False)) return self.__support_legacy @@ -146,13 +146,13 @@ class SdistBuilder(BuilderInterface): Build an archive of the source files """ - PLUGIN_NAME = 'sdist' + PLUGIN_NAME = "sdist" def get_version_api(self) -> dict[str, Callable]: - return {'standard': self.build_standard} + return {"standard": self.build_standard} def get_default_versions(self) -> list[str]: # noqa: PLR6301 - return ['standard'] + return ["standard"] def clean( # noqa: PLR6301 self, @@ -160,7 +160,7 @@ def clean( # noqa: PLR6301 versions: list[str], # noqa: ARG002 ) -> None: for filename in os.listdir(directory): - if filename.endswith('.tar.gz'): + if filename.endswith(".tar.gz"): os.remove(os.path.join(directory, filename)) def build_standard(self, directory: str, **build_data: Any) -> str: @@ -170,7 +170,7 @@ def build_standard(self, directory: str, **build_data: Any) -> str: for included_file in self.recurse_included_files(): if self.config.support_legacy: possible_package, file_name = os.path.split(included_file.relative_path) - if file_name == '__init__.py': + if file_name == "__init__.py": found_packages.add(possible_package) tar_info = archive.gettarinfo( @@ -183,24 +183,24 @@ def build_standard(self, directory: str, **build_data: Any) -> str: continue if tar_info.isfile(): - with open(included_file.path, 'rb') as f: + with open(included_file.path, "rb") as f: archive.addfile(tar_info, f) else: # no cov # TODO: Investigate if this is necessary (for symlinks, etc.) archive.addfile(tar_info) archive.create_file( - self.config.core_metadata_constructor(self.metadata, extra_dependencies=build_data['dependencies']), - 'PKG-INFO', + self.config.core_metadata_constructor(self.metadata, extra_dependencies=build_data["dependencies"]), + "PKG-INFO", ) if self.config.support_legacy: archive.create_file( - self.construct_setup_py_file(sorted(found_packages), extra_dependencies=build_data['dependencies']), - 'setup.py', + self.construct_setup_py_file(sorted(found_packages), extra_dependencies=build_data["dependencies"]), + "setup.py", ) - target = os.path.join(directory, f'{self.artifact_project_id}.tar.gz') + target = os.path.join(directory, f"{self.artifact_project_id}.tar.gz") replace_file(archive.path, target) normalize_artifact_permissions(target) @@ -211,73 +211,73 @@ def artifact_project_id(self) -> str: return ( self.project_id if self.config.strict_naming - else f'{self.normalize_file_name_component(self.metadata.core.raw_name)}-{self.metadata.version}' + else f"{self.normalize_file_name_component(self.metadata.core.raw_name)}-{self.metadata.version}" ) def construct_setup_py_file(self, packages: list[str], extra_dependencies: tuple[()] = ()) -> str: - contents = 'from setuptools import setup\n\n' + contents = "from setuptools import setup\n\n" - contents += 'setup(\n' + contents += "setup(\n" - contents += f' name={self.metadata.core.name!r},\n' - contents += f' version={self.metadata.version!r},\n' + contents += f" name={self.metadata.core.name!r},\n" + contents += f" version={self.metadata.version!r},\n" if self.metadata.core.description: - contents += f' description={self.metadata.core.description!r},\n' + contents += f" description={self.metadata.core.description!r},\n" if self.metadata.core.readme: - contents += f' long_description={self.metadata.core.readme!r},\n' + contents += f" long_description={self.metadata.core.readme!r},\n" authors_data = self.metadata.core.authors_data - if authors_data['name']: + if authors_data["name"]: contents += f" author={', '.join(authors_data['name'])!r},\n" - if authors_data['email']: + if authors_data["email"]: contents += f" author_email={', '.join(authors_data['email'])!r},\n" maintainers_data = self.metadata.core.maintainers_data - if maintainers_data['name']: + if maintainers_data["name"]: contents += f" maintainer={', '.join(maintainers_data['name'])!r},\n" - if maintainers_data['email']: + if maintainers_data["email"]: contents += f" maintainer_email={', '.join(maintainers_data['email'])!r},\n" if self.metadata.core.classifiers: - contents += ' classifiers=[\n' + contents += " classifiers=[\n" for classifier in self.metadata.core.classifiers: - contents += f' {classifier!r},\n' + contents += f" {classifier!r},\n" - contents += ' ],\n' + contents += " ],\n" dependencies = list(self.metadata.core.dependencies) dependencies.extend(extra_dependencies) if dependencies: - contents += ' install_requires=[\n' + contents += " install_requires=[\n" for raw_specifier in dependencies: specifier = raw_specifier.replace("'", '"') - contents += f' {specifier!r},\n' + contents += f" {specifier!r},\n" - contents += ' ],\n' + contents += " ],\n" if self.metadata.core.optional_dependencies: - contents += ' extras_require={\n' + contents += " extras_require={\n" for option, specifiers in self.metadata.core.optional_dependencies.items(): if not specifiers: continue - contents += f' {option!r}: [\n' + contents += f" {option!r}: [\n" for raw_specifier in specifiers: specifier = raw_specifier.replace("'", '"') - contents += f' {specifier!r},\n' + contents += f" {specifier!r},\n" - contents += ' ],\n' + contents += " ],\n" - contents += ' },\n' + contents += " },\n" if self.metadata.core.scripts or self.metadata.core.gui_scripts or self.metadata.core.entry_points: - contents += ' entry_points={\n' + contents += " entry_points={\n" if self.metadata.core.scripts: contents += " 'console_scripts': [\n" @@ -285,7 +285,7 @@ def construct_setup_py_file(self, packages: list[str], extra_dependencies: tuple for name, object_ref in self.metadata.core.scripts.items(): contents += f" '{name} = {object_ref}',\n" - contents += ' ],\n' + contents += " ],\n" if self.metadata.core.gui_scripts: contents += " 'gui_scripts': [\n" @@ -293,46 +293,46 @@ def construct_setup_py_file(self, packages: list[str], extra_dependencies: tuple for name, object_ref in self.metadata.core.gui_scripts.items(): contents += f" '{name} = {object_ref}',\n" - contents += ' ],\n' + contents += " ],\n" if self.metadata.core.entry_points: for group, entry_points in self.metadata.core.entry_points.items(): - contents += f' {group!r}: [\n' + contents += f" {group!r}: [\n" for name, object_ref in entry_points.items(): contents += f" '{name} = {object_ref}',\n" - contents += ' ],\n' + contents += " ],\n" - contents += ' },\n' + contents += " },\n" if packages: src_layout = False - contents += ' packages=[\n' + contents += " packages=[\n" for package in packages: - if package.startswith(f'src{os.sep}'): + if package.startswith(f"src{os.sep}"): src_layout = True contents += f" {package.replace(os.sep, '.')[4:]!r},\n" else: contents += f" {package.replace(os.sep, '.')!r},\n" - contents += ' ],\n' + contents += " ],\n" if src_layout: contents += " package_dir={'': 'src'},\n" - contents += ')\n' + contents += ")\n" return contents def get_default_build_data(self) -> dict[str, Any]: force_include = {} - for filename in ['pyproject.toml', DEFAULT_CONFIG_FILE, DEFAULT_BUILD_SCRIPT]: + for filename in ["pyproject.toml", DEFAULT_CONFIG_FILE, DEFAULT_BUILD_SCRIPT]: path = os.path.join(self.root, filename) if os.path.exists(path): force_include[path] = filename - build_data = {'force_include': force_include, 'dependencies': []} + build_data = {"force_include": force_include, "dependencies": []} for exclusion_files in self.config.vcs_exclusion_files.values(): for exclusion_file in exclusion_files: diff --git a/backend/src/hatchling/builders/utils.py b/backend/src/hatchling/builders/utils.py index ffbd8e7cf..f462073b8 100644 --- a/backend/src/hatchling/builders/utils.py +++ b/backend/src/hatchling/builders/utils.py @@ -40,8 +40,8 @@ def get_relative_path(path: str, start: str) -> str: relative_path = os.path.relpath(path, start) # First iteration of `os.walk` - if relative_path == '.': - return '' + if relative_path == ".": + return "" return relative_path @@ -73,15 +73,15 @@ def normalize_inclusion_map(inclusion_map: dict[str, str], root: str) -> dict[st def normalize_archive_path(path: str) -> str: - if os.sep != '/': - return path.replace(os.sep, '/') + if os.sep != "/": + return path.replace(os.sep, "/") return path def format_file_hash(digest: bytes) -> str: # https://peps.python.org/pep-0427/#signed-wheel-files - return urlsafe_b64encode(digest).decode('ascii').rstrip('=') + return urlsafe_b64encode(digest).decode("ascii").rstrip("=") def get_reproducible_timestamp() -> int: @@ -91,7 +91,7 @@ def get_reproducible_timestamp() -> int: The default value will always be: `1580601600` """ - return int(os.environ.get('SOURCE_DATE_EPOCH', '1580601600')) + return int(os.environ.get("SOURCE_DATE_EPOCH", "1580601600")) def normalize_file_permissions(st_mode: int) -> int: diff --git a/backend/src/hatchling/builders/wheel.py b/backend/src/hatchling/builders/wheel.py index fd524094c..cf7c236af 100644 --- a/backend/src/hatchling/builders/wheel.py +++ b/backend/src/hatchling/builders/wheel.py @@ -48,7 +48,7 @@ class FileSelectionOptions(NamedTuple): class RecordFile: def __init__(self) -> None: self.__file_obj = StringIO() - self.__writer = csv.writer(self.__file_obj, delimiter=',', quotechar='"', lineterminator='\n') + self.__writer = csv.writer(self.__file_obj, delimiter=",", quotechar='"', lineterminator="\n") def write(self, record: Iterable[Any]) -> None: self.__writer.writerow(record) @@ -70,8 +70,8 @@ def __init__(self, project_id: str, *, reproducible: bool) -> None: """ https://peps.python.org/pep-0427/#abstract """ - self.metadata_directory = f'{project_id}.dist-info' - self.shared_data_directory = f'{project_id}.data' + self.metadata_directory = f"{project_id}.dist-info" + self.shared_data_directory = f"{project_id}.data" self.time_tuple: TIME_TUPLE | None = None self.reproducible = reproducible @@ -80,9 +80,9 @@ def __init__(self, project_id: str, *, reproducible: bool) -> None: else: self.time_tuple = None - raw_fd, self.path = tempfile.mkstemp(suffix='.whl') - self.fd = os.fdopen(raw_fd, 'w+b') - self.zf = zipfile.ZipFile(self.fd, 'w', compression=zipfile.ZIP_DEFLATED) + raw_fd, self.path = tempfile.mkstemp(suffix=".whl") + self.fd = os.fdopen(raw_fd, "w+b") + self.zf = zipfile.ZipFile(self.fd, "w", compression=zipfile.ZIP_DEFLATED) @staticmethod def get_reproducible_time_tuple() -> TIME_TUPLE: @@ -111,7 +111,7 @@ def add_file(self, included_file: IncludedFile) -> tuple[str, str, str]: zip_info.compress_type = zipfile.ZIP_DEFLATED hash_obj = hashlib.sha256() - with open(included_file.path, 'rb') as in_file, self.zf.open(zip_info, 'w') as out_file: + with open(included_file.path, "rb") as in_file, self.zf.open(zip_info, "w") as out_file: while True: chunk = in_file.read(16384) if not chunk: @@ -121,17 +121,17 @@ def add_file(self, included_file: IncludedFile) -> tuple[str, str, str]: out_file.write(chunk) hash_digest = format_file_hash(hash_obj.digest()) - return relative_path, f'sha256={hash_digest}', str(file_stat.st_size) + return relative_path, f"sha256={hash_digest}", str(file_stat.st_size) def write_metadata(self, relative_path: str, contents: str | bytes) -> tuple[str, str, str]: - relative_path = f'{self.metadata_directory}/{normalize_archive_path(relative_path)}' + relative_path = f"{self.metadata_directory}/{normalize_archive_path(relative_path)}" return self.write_file(relative_path, contents) def write_shared_script(self, included_file: IncludedFile, contents: str | bytes) -> tuple[str, str, str]: relative_path = ( - f'{self.shared_data_directory}/scripts/{normalize_archive_path(included_file.distribution_path)}' + f"{self.shared_data_directory}/scripts/{normalize_archive_path(included_file.distribution_path)}" ) - if sys.platform == 'win32': + if sys.platform == "win32": return self.write_file(relative_path, contents) file_stat = os.stat(included_file.path) @@ -142,12 +142,12 @@ def write_shared_script(self, included_file: IncludedFile, contents: str | bytes ) def add_shared_file(self, shared_file: IncludedFile) -> tuple[str, str, str]: - shared_file.distribution_path = f'{self.shared_data_directory}/data/{shared_file.distribution_path}' + shared_file.distribution_path = f"{self.shared_data_directory}/data/{shared_file.distribution_path}" return self.add_file(shared_file) def add_extra_metadata_file(self, extra_metadata_file: IncludedFile) -> tuple[str, str, str]: extra_metadata_file.distribution_path = ( - f'{self.metadata_directory}/extra_metadata/{extra_metadata_file.distribution_path}' + f"{self.metadata_directory}/extra_metadata/{extra_metadata_file.distribution_path}" ) return self.add_file(extra_metadata_file) @@ -159,7 +159,7 @@ def write_file( mode: int | None = None, ) -> tuple[str, str, str]: if not isinstance(contents, bytes): - contents = contents.encode('utf-8') + contents = contents.encode("utf-8") time_tuple = self.time_tuple or (2020, 2, 2, 0, 0, 0) zip_info = zipfile.ZipInfo(relative_path, time_tuple) @@ -172,7 +172,7 @@ def write_file( hash_digest = format_file_hash(hash_obj.digest()) self.zf.writestr(zip_info, contents, compress_type=zipfile.ZIP_DEFLATED) - return relative_path, f'sha256={hash_digest}', str(len(contents)) + return relative_path, f"sha256={hash_digest}", str(len(contents)) def __enter__(self) -> WheelArchive: # noqa: PYI034 return self @@ -197,10 +197,10 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: @cached_property def default_file_selection_options(self) -> FileSelectionOptions: - include = self.target_config.get('include', self.build_config.get('include', [])) - exclude = self.target_config.get('exclude', self.build_config.get('exclude', [])) - packages = self.target_config.get('packages', self.build_config.get('packages', [])) - only_include = self.target_config.get('only-include', self.build_config.get('only-include', [])) + include = self.target_config.get("include", self.build_config.get("include", [])) + exclude = self.target_config.get("exclude", self.build_config.get("exclude", [])) + packages = self.target_config.get("packages", self.build_config.get("packages", [])) + only_include = self.target_config.get("only-include", self.build_config.get("only-include", [])) if include or packages or only_include: return FileSelectionOptions(include, exclude, packages, only_include) @@ -210,21 +210,21 @@ def default_file_selection_options(self) -> FileSelectionOptions: self.builder.normalize_file_name_component(self.builder.metadata.core.raw_name), self.builder.normalize_file_name_component(self.builder.metadata.core.name), ): - if os.path.isfile(os.path.join(self.root, project_name, '__init__.py')): + if os.path.isfile(os.path.join(self.root, project_name, "__init__.py")): normalized_project_name = self.get_raw_fs_path_name(self.root, project_name) return FileSelectionOptions([], exclude, [normalized_project_name], []) - if os.path.isfile(os.path.join(self.root, 'src', project_name, '__init__.py')): - normalized_project_name = self.get_raw_fs_path_name(os.path.join(self.root, 'src'), project_name) - return FileSelectionOptions([], exclude, [f'src/{normalized_project_name}'], []) + if os.path.isfile(os.path.join(self.root, "src", project_name, "__init__.py")): + normalized_project_name = self.get_raw_fs_path_name(os.path.join(self.root, "src"), project_name) + return FileSelectionOptions([], exclude, [f"src/{normalized_project_name}"], []) - module_file = f'{project_name}.py' + module_file = f"{project_name}.py" if os.path.isfile(os.path.join(self.root, module_file)): return FileSelectionOptions([], exclude, [], [module_file]) from glob import glob - possible_namespace_packages = glob(os.path.join(self.root, '*', project_name, '__init__.py')) + possible_namespace_packages = glob(os.path.join(self.root, "*", project_name, "__init__.py")) if len(possible_namespace_packages) == 1: relative_path = os.path.relpath(possible_namespace_packages[0], self.root) namespace = relative_path.split(os.sep)[0] @@ -235,17 +235,17 @@ def default_file_selection_options(self) -> FileSelectionOptions: self.set_exclude_all() return FileSelectionOptions([], exclude, [], []) - project_names_text = ' or '.join(sorted(project_names)) + project_names_text = " or ".join(sorted(project_names)) message = ( - f'Unable to determine which files to ship inside the wheel using the following heuristics: ' - f'https://hatch.pypa.io/latest/plugins/builder/wheel/#default-file-selection\n\n' - f'The most likely cause of this is that there is no directory that matches the name of your ' - f'project ({project_names_text}).\n\n' - f'At least one file selection option must be defined in the `tool.hatch.build.targets.wheel` ' - f'table, see: https://hatch.pypa.io/latest/config/build/\n\n' - f'As an example, if you intend to ship a directory named `foo` that resides within a `src` ' - f'directory located at the root of your project, you can define the following:\n\n' - f'[tool.hatch.build.targets.wheel]\n' + f"Unable to determine which files to ship inside the wheel using the following heuristics: " + f"https://hatch.pypa.io/latest/plugins/builder/wheel/#default-file-selection\n\n" + f"The most likely cause of this is that there is no directory that matches the name of your " + f"project ({project_names_text}).\n\n" + f"At least one file selection option must be defined in the `tool.hatch.build.targets.wheel` " + f"table, see: https://hatch.pypa.io/latest/config/build/\n\n" + f"As an example, if you intend to ship a directory named `foo` that resides within a `src` " + f"directory located at the root of your project, you can define the following:\n\n" + f"[tool.hatch.build.targets.wheel]\n" f'packages = ["src/foo"]' ) raise ValueError(message) @@ -265,17 +265,17 @@ def default_only_include(self) -> list[str]: @property def core_metadata_constructor(self) -> Callable[..., str]: if self.__core_metadata_constructor is None: - core_metadata_version = self.target_config.get('core-metadata-version', DEFAULT_METADATA_VERSION) + core_metadata_version = self.target_config.get("core-metadata-version", DEFAULT_METADATA_VERSION) if not isinstance(core_metadata_version, str): - message = f'Field `tool.hatch.build.targets.{self.plugin_name}.core-metadata-version` must be a string' + message = f"Field `tool.hatch.build.targets.{self.plugin_name}.core-metadata-version` must be a string" raise TypeError(message) constructors = get_core_metadata_constructors() if core_metadata_version not in constructors: message = ( - f'Unknown metadata version `{core_metadata_version}` for field ' - f'`tool.hatch.build.targets.{self.plugin_name}.core-metadata-version`. ' - f'Available: {", ".join(sorted(constructors))}' + f"Unknown metadata version `{core_metadata_version}` for field " + f"`tool.hatch.build.targets.{self.plugin_name}.core-metadata-version`. " + f"Available: {', '.join(sorted(constructors))}" ) raise ValueError(message) @@ -286,30 +286,30 @@ def core_metadata_constructor(self) -> Callable[..., str]: @property def shared_data(self) -> dict[str, str]: if self.__shared_data is None: - shared_data = self.target_config.get('shared-data', {}) + shared_data = self.target_config.get("shared-data", {}) if not isinstance(shared_data, dict): - message = f'Field `tool.hatch.build.targets.{self.plugin_name}.shared-data` must be a mapping' + message = f"Field `tool.hatch.build.targets.{self.plugin_name}.shared-data` must be a mapping" raise TypeError(message) for i, (source, relative_path) in enumerate(shared_data.items(), 1): if not source: message = ( - f'Source #{i} in field `tool.hatch.build.targets.{self.plugin_name}.shared-data` ' - f'cannot be an empty string' + f"Source #{i} in field `tool.hatch.build.targets.{self.plugin_name}.shared-data` " + f"cannot be an empty string" ) raise ValueError(message) if not isinstance(relative_path, str): message = ( - f'Path for source `{source}` in field ' - f'`tool.hatch.build.targets.{self.plugin_name}.shared-data` must be a string' + f"Path for source `{source}` in field " + f"`tool.hatch.build.targets.{self.plugin_name}.shared-data` must be a string" ) raise TypeError(message) if not relative_path: message = ( - f'Path for source `{source}` in field ' - f'`tool.hatch.build.targets.{self.plugin_name}.shared-data` cannot be an empty string' + f"Path for source `{source}` in field " + f"`tool.hatch.build.targets.{self.plugin_name}.shared-data` cannot be an empty string" ) raise ValueError(message) @@ -320,30 +320,30 @@ def shared_data(self) -> dict[str, str]: @property def shared_scripts(self) -> dict[str, str]: if self.__shared_scripts is None: - shared_scripts = self.target_config.get('shared-scripts', {}) + shared_scripts = self.target_config.get("shared-scripts", {}) if not isinstance(shared_scripts, dict): - message = f'Field `tool.hatch.build.targets.{self.plugin_name}.shared-scripts` must be a mapping' + message = f"Field `tool.hatch.build.targets.{self.plugin_name}.shared-scripts` must be a mapping" raise TypeError(message) for i, (source, relative_path) in enumerate(shared_scripts.items(), 1): if not source: message = ( - f'Source #{i} in field `tool.hatch.build.targets.{self.plugin_name}.shared-scripts` ' - f'cannot be an empty string' + f"Source #{i} in field `tool.hatch.build.targets.{self.plugin_name}.shared-scripts` " + f"cannot be an empty string" ) raise ValueError(message) if not isinstance(relative_path, str): message = ( - f'Path for source `{source}` in field ' - f'`tool.hatch.build.targets.{self.plugin_name}.shared-scripts` must be a string' + f"Path for source `{source}` in field " + f"`tool.hatch.build.targets.{self.plugin_name}.shared-scripts` must be a string" ) raise TypeError(message) if not relative_path: message = ( - f'Path for source `{source}` in field ' - f'`tool.hatch.build.targets.{self.plugin_name}.shared-scripts` cannot be an empty string' + f"Path for source `{source}` in field " + f"`tool.hatch.build.targets.{self.plugin_name}.shared-scripts` cannot be an empty string" ) raise ValueError(message) @@ -354,30 +354,30 @@ def shared_scripts(self) -> dict[str, str]: @property def extra_metadata(self) -> dict[str, str]: if self.__extra_metadata is None: - extra_metadata = self.target_config.get('extra-metadata', {}) + extra_metadata = self.target_config.get("extra-metadata", {}) if not isinstance(extra_metadata, dict): - message = f'Field `tool.hatch.build.targets.{self.plugin_name}.extra-metadata` must be a mapping' + message = f"Field `tool.hatch.build.targets.{self.plugin_name}.extra-metadata` must be a mapping" raise TypeError(message) for i, (source, relative_path) in enumerate(extra_metadata.items(), 1): if not source: message = ( - f'Source #{i} in field `tool.hatch.build.targets.{self.plugin_name}.extra-metadata` ' - f'cannot be an empty string' + f"Source #{i} in field `tool.hatch.build.targets.{self.plugin_name}.extra-metadata` " + f"cannot be an empty string" ) raise ValueError(message) if not isinstance(relative_path, str): message = ( - f'Path for source `{source}` in field ' - f'`tool.hatch.build.targets.{self.plugin_name}.extra-metadata` must be a string' + f"Path for source `{source}` in field " + f"`tool.hatch.build.targets.{self.plugin_name}.extra-metadata` must be a string" ) raise TypeError(message) if not relative_path: message = ( - f'Path for source `{source}` in field ' - f'`tool.hatch.build.targets.{self.plugin_name}.extra-metadata` cannot be an empty string' + f"Path for source `{source}` in field " + f"`tool.hatch.build.targets.{self.plugin_name}.extra-metadata` cannot be an empty string" ) raise ValueError(message) @@ -388,15 +388,15 @@ def extra_metadata(self) -> dict[str, str]: @property def strict_naming(self) -> bool: if self.__strict_naming is None: - if 'strict-naming' in self.target_config: - strict_naming = self.target_config['strict-naming'] + if "strict-naming" in self.target_config: + strict_naming = self.target_config["strict-naming"] if not isinstance(strict_naming, bool): - message = f'Field `tool.hatch.build.targets.{self.plugin_name}.strict-naming` must be a boolean' + message = f"Field `tool.hatch.build.targets.{self.plugin_name}.strict-naming` must be a boolean" raise TypeError(message) else: - strict_naming = self.build_config.get('strict-naming', True) + strict_naming = self.build_config.get("strict-naming", True) if not isinstance(strict_naming, bool): - message = 'Field `tool.hatch.build.strict-naming` must be a boolean' + message = "Field `tool.hatch.build.strict-naming` must be a boolean" raise TypeError(message) self.__strict_naming = strict_naming @@ -406,9 +406,9 @@ def strict_naming(self) -> bool: @property def macos_max_compat(self) -> bool: if self.__macos_max_compat is None: - macos_max_compat = self.target_config.get('macos-max-compat', False) + macos_max_compat = self.target_config.get("macos-max-compat", False) if not isinstance(macos_max_compat, bool): - message = f'Field `tool.hatch.build.targets.{self.plugin_name}.macos-max-compat` must be a boolean' + message = f"Field `tool.hatch.build.targets.{self.plugin_name}.macos-max-compat` must be a boolean" raise TypeError(message) self.__macos_max_compat = macos_max_compat @@ -417,14 +417,14 @@ def macos_max_compat(self) -> bool: @cached_property def bypass_selection(self) -> bool: - bypass_selection = self.target_config.get('bypass-selection', False) + bypass_selection = self.target_config.get("bypass-selection", False) if not isinstance(bypass_selection, bool): - message = f'Field `tool.hatch.build.targets.{self.plugin_name}.bypass-selection` must be a boolean' + message = f"Field `tool.hatch.build.targets.{self.plugin_name}.bypass-selection` must be a boolean" raise TypeError(message) return bypass_selection - if sys.platform in {'darwin', 'win32'}: + if sys.platform in {"darwin", "win32"}: @staticmethod def get_raw_fs_path_name(directory: str, name: str) -> str: @@ -448,13 +448,13 @@ class WheelBuilder(BuilderInterface): Build a binary distribution (.whl file) """ - PLUGIN_NAME = 'wheel' + PLUGIN_NAME = "wheel" def get_version_api(self) -> dict[str, Callable]: - return {'standard': self.build_standard, 'editable': self.build_editable} + return {"standard": self.build_standard, "editable": self.build_editable} def get_default_versions(self) -> list[str]: # noqa: PLR6301 - return ['standard'] + return ["standard"] def clean( # noqa: PLR6301 self, @@ -462,27 +462,28 @@ def clean( # noqa: PLR6301 versions: list[str], # noqa: ARG002 ) -> None: for filename in os.listdir(directory): - if filename.endswith('.whl'): + if filename.endswith(".whl"): os.remove(os.path.join(directory, filename)) def build_standard(self, directory: str, **build_data: Any) -> str: - if 'tag' not in build_data: - if build_data['infer_tag']: - build_data['tag'] = self.get_best_matching_tag() + if "tag" not in build_data: + if build_data["infer_tag"]: + build_data["tag"] = self.get_best_matching_tag() else: - build_data['tag'] = self.get_default_tag() + build_data["tag"] = self.get_default_tag() - with WheelArchive( - self.artifact_project_id, reproducible=self.config.reproducible - ) as archive, RecordFile() as records: + with ( + WheelArchive(self.artifact_project_id, reproducible=self.config.reproducible) as archive, + RecordFile() as records, + ): for included_file in self.recurse_included_files(): record = archive.add_file(included_file) records.write(record) - self.write_data(archive, records, build_data, build_data['dependencies']) + self.write_data(archive, records, build_data, build_data["dependencies"]) - records.write((f'{archive.metadata_directory}/RECORD', '', '')) - archive.write_metadata('RECORD', records.construct()) + records.write((f"{archive.metadata_directory}/RECORD", "", "")) + archive.write_metadata("RECORD", records.construct()) target = os.path.join(directory, f"{self.artifact_project_id}-{build_data['tag']}.whl") @@ -499,14 +500,15 @@ def build_editable(self, directory: str, **build_data: Any) -> str: def build_editable_detection(self, directory: str, **build_data: Any) -> str: from editables import EditableProject - build_data['tag'] = self.get_default_tag() + build_data["tag"] = self.get_default_tag() - with WheelArchive( - self.artifact_project_id, reproducible=self.config.reproducible - ) as archive, RecordFile() as records: + with ( + WheelArchive(self.artifact_project_id, reproducible=self.config.reproducible) as archive, + RecordFile() as records, + ): exposed_packages = {} for included_file in self.recurse_selected_project_files(): - if not included_file.path.endswith('.py'): + if not included_file.path.endswith(".py"): continue relative_path = included_file.relative_path @@ -527,13 +529,13 @@ def build_editable_detection(self, directory: str, **build_data: Any) -> str: try: exposed_packages[distribution_module] = os.path.join( self.root, - f'{relative_path[: relative_path.index(distribution_path)]}{distribution_module}', + f"{relative_path[: relative_path.index(distribution_path)]}{distribution_module}", ) except ValueError: message = ( - 'Dev mode installations are unsupported when any path rewrite in the `sources` option ' - 'changes a prefix rather than removes it, see: ' - 'https://github.com/pfmoore/editables/issues/20' + "Dev mode installations are unsupported when any path rewrite in the `sources` option " + "changes a prefix rather than removes it, see: " + "https://github.com/pfmoore/editables/issues/20" ) raise ValueError(message) from None @@ -548,8 +550,8 @@ def build_editable_detection(self, directory: str, **build_data: Any) -> str: for raw_filename, content in sorted(editable_project.files()): filename = raw_filename - if filename.endswith('.pth') and not filename.startswith('_'): - filename = f'_{filename}' + if filename.endswith(".pth") and not filename.startswith("_"): + filename = f"_{filename}" record = archive.write_file(filename, content) records.write(record) @@ -558,10 +560,10 @@ def build_editable_detection(self, directory: str, **build_data: Any) -> str: record = archive.add_file(included_file) records.write(record) - extra_dependencies = list(build_data['dependencies']) + extra_dependencies = list(build_data["dependencies"]) for raw_dependency in editable_project.dependencies(): dependency = raw_dependency - if dependency == 'editables': + if dependency == "editables": dependency = EDITABLES_REQUIREMENT else: # no cov pass @@ -570,8 +572,8 @@ def build_editable_detection(self, directory: str, **build_data: Any) -> str: self.write_data(archive, records, build_data, extra_dependencies) - records.write((f'{archive.metadata_directory}/RECORD', '', '')) - archive.write_metadata('RECORD', records.construct()) + records.write((f"{archive.metadata_directory}/RECORD", "", "")) + archive.write_metadata("RECORD", records.construct()) target = os.path.join(directory, f"{self.artifact_project_id}-{build_data['tag']}.whl") @@ -580,27 +582,28 @@ def build_editable_detection(self, directory: str, **build_data: Any) -> str: return target def build_editable_explicit(self, directory: str, **build_data: Any) -> str: - build_data['tag'] = self.get_default_tag() + build_data["tag"] = self.get_default_tag() - with WheelArchive( - self.artifact_project_id, reproducible=self.config.reproducible - ) as archive, RecordFile() as records: + with ( + WheelArchive(self.artifact_project_id, reproducible=self.config.reproducible) as archive, + RecordFile() as records, + ): directories = sorted( os.path.normpath(os.path.join(self.root, relative_directory)) for relative_directory in self.config.dev_mode_dirs ) - record = archive.write_file(f"_{self.metadata.core.name.replace('-', '_')}.pth", '\n'.join(directories)) + record = archive.write_file(f"_{self.metadata.core.name.replace('-', '_')}.pth", "\n".join(directories)) records.write(record) for included_file in self.recurse_forced_files(self.get_forced_inclusion_map(build_data)): record = archive.add_file(included_file) records.write(record) - self.write_data(archive, records, build_data, build_data['dependencies']) + self.write_data(archive, records, build_data, build_data["dependencies"]) - records.write((f'{archive.metadata_directory}/RECORD', '', '')) - archive.write_metadata('RECORD', records.construct()) + records.write((f"{archive.metadata_directory}/RECORD", "", "")) + archive.write_metadata("RECORD", records.construct()) target = os.path.join(directory, f"{self.artifact_project_id}-{build_data['tag']}.whl") @@ -619,7 +622,7 @@ def write_data( def add_shared_data(self, archive: WheelArchive, records: RecordFile, build_data: dict[str, Any]) -> None: shared_data = dict(self.config.shared_data) - shared_data.update(normalize_inclusion_map(build_data['shared_data'], self.root)) + shared_data.update(normalize_inclusion_map(build_data["shared_data"], self.root)) for shared_file in self.recurse_explicit_files(shared_data): record = archive.add_shared_file(shared_file) @@ -630,13 +633,13 @@ def add_shared_scripts(self, archive: WheelArchive, records: RecordFile, build_d from io import BytesIO # https://packaging.python.org/en/latest/specifications/binary-distribution-format/#recommended-installer-features - shebang = re.compile(rb'^#!.*(?:pythonw?|pypyw?)[0-9.]*(.*)', flags=re.DOTALL) + shebang = re.compile(rb"^#!.*(?:pythonw?|pypyw?)[0-9.]*(.*)", flags=re.DOTALL) shared_scripts = dict(self.config.shared_scripts) - shared_scripts.update(normalize_inclusion_map(build_data['shared_scripts'], self.root)) + shared_scripts.update(normalize_inclusion_map(build_data["shared_scripts"], self.root)) for shared_script in self.recurse_explicit_files(shared_scripts): - with open(shared_script.path, 'rb') as f: + with open(shared_script.path, "rb") as f: content = BytesIO() for line in f: # Ignore leading blank lines @@ -647,7 +650,7 @@ def add_shared_scripts(self, archive: WheelArchive, records: RecordFile, build_d if match is None: content.write(line) else: - content.write(b'#!python') + content.write(b"#!python") if remaining := match.group(1): content.write(remaining) @@ -689,39 +692,39 @@ def write_archive_metadata(archive: WheelArchive, records: RecordFile, build_dat metadata = f"""\ Wheel-Version: 1.0 Generator: hatchling {__version__} -Root-Is-Purelib: {'true' if build_data['pure_python'] else 'false'} +Root-Is-Purelib: {"true" if build_data["pure_python"] else "false"} """ - for tag in sorted(map(str, parse_tag(build_data['tag']))): - metadata += f'Tag: {tag}\n' + for tag in sorted(map(str, parse_tag(build_data["tag"]))): + metadata += f"Tag: {tag}\n" - record = archive.write_metadata('WHEEL', metadata) + record = archive.write_metadata("WHEEL", metadata) records.write(record) def write_entry_points_file(self, archive: WheelArchive, records: RecordFile) -> None: entry_points_file = self.construct_entry_points_file() if entry_points_file: - record = archive.write_metadata('entry_points.txt', entry_points_file) + record = archive.write_metadata("entry_points.txt", entry_points_file) records.write(record) def write_project_metadata( self, archive: WheelArchive, records: RecordFile, extra_dependencies: Sequence[str] = () ) -> None: record = archive.write_metadata( - 'METADATA', self.config.core_metadata_constructor(self.metadata, extra_dependencies=extra_dependencies) + "METADATA", self.config.core_metadata_constructor(self.metadata, extra_dependencies=extra_dependencies) ) records.write(record) def add_licenses(self, archive: WheelArchive, records: RecordFile) -> None: for relative_path in self.metadata.core.license_files: license_file = os.path.normpath(os.path.join(self.root, relative_path)) - with open(license_file, 'rb') as f: - record = archive.write_metadata(f'licenses/{relative_path}', f.read()) + with open(license_file, "rb") as f: + record = archive.write_metadata(f"licenses/{relative_path}", f.read()) records.write(record) def add_extra_metadata(self, archive: WheelArchive, records: RecordFile, build_data: dict[str, Any]) -> None: extra_metadata = dict(self.config.extra_metadata) - extra_metadata.update(normalize_inclusion_map(build_data['extra_metadata'], self.root)) + extra_metadata.update(normalize_inclusion_map(build_data["extra_metadata"], self.root)) for extra_metadata_file in self.recurse_explicit_files(extra_metadata): record = archive.add_extra_metadata_file(extra_metadata_file) @@ -729,23 +732,23 @@ def add_extra_metadata(self, archive: WheelArchive, records: RecordFile, build_d def construct_entry_points_file(self) -> str: core_metadata = self.metadata.core - metadata_file = '' + metadata_file = "" if core_metadata.scripts: - metadata_file += '\n[console_scripts]\n' + metadata_file += "\n[console_scripts]\n" for name, object_ref in core_metadata.scripts.items(): - metadata_file += f'{name} = {object_ref}\n' + metadata_file += f"{name} = {object_ref}\n" if core_metadata.gui_scripts: - metadata_file += '\n[gui_scripts]\n' + metadata_file += "\n[gui_scripts]\n" for name, object_ref in core_metadata.gui_scripts.items(): - metadata_file += f'{name} = {object_ref}\n' + metadata_file += f"{name} = {object_ref}\n" if core_metadata.entry_points: for group, entry_points in core_metadata.entry_points.items(): - metadata_file += f'\n[{group}]\n' + metadata_file += f"\n[{group}]\n" for name, object_ref in entry_points.items(): - metadata_file += f'{name} = {object_ref}\n' + metadata_file += f"{name} = {object_ref}\n" return metadata_file.lstrip() @@ -756,8 +759,8 @@ def get_default_tag(self) -> str: for major_version in known_major_versions: for minor_version in range(max_version_part): # Try an artificially high patch version to account for common cases like `>=3.11.4` or `>=3.10,<3.11` - if self.metadata.core.python_constraint.contains(f'{major_version}.{minor_version}.{max_version_part}'): - supported_python_versions.append(f'py{major_version}') + if self.metadata.core.python_constraint.contains(f"{major_version}.{minor_version}.{max_version_part}"): + supported_python_versions.append(f"py{major_version}") break # Slow path, try all permutations to account for narrow support ranges like `<=3.11.4` @@ -766,15 +769,15 @@ def get_default_tag(self) -> str: for minor_version in range(max_version_part): for patch_version in range(max_version_part): if self.metadata.core.python_constraint.contains( - f'{major_version}.{minor_version}.{patch_version}' + f"{major_version}.{minor_version}.{patch_version}" ): - supported_python_versions.append(f'py{major_version}') + supported_python_versions.append(f"py{major_version}") break else: continue break - return f'{".".join(supported_python_versions)}-none-any' + return f"{'.'.join(supported_python_versions)}-none-any" def get_best_matching_tag(self) -> str: import sys @@ -783,39 +786,39 @@ def get_best_matching_tag(self) -> str: # Linux tag is after many/musl; packaging tools are required to skip # many/musl, see https://github.com/pypa/packaging/issues/160 - tag = next(iter(t for t in sys_tags() if 'manylinux' not in t.platform and 'musllinux' not in t.platform)) + tag = next(iter(t for t in sys_tags() if "manylinux" not in t.platform and "musllinux" not in t.platform)) tag_parts = [tag.interpreter, tag.abi, tag.platform] - if sys.platform == 'darwin': + if sys.platform == "darwin": from hatchling.builders.macos import process_macos_plat_tag tag_parts[2] = process_macos_plat_tag(tag_parts[2], compat=self.config.macos_max_compat) - return '-'.join(tag_parts) + return "-".join(tag_parts) def get_default_build_data(self) -> dict[str, Any]: # noqa: PLR6301 return { - 'infer_tag': False, - 'pure_python': True, - 'dependencies': [], - 'force_include_editable': {}, - 'extra_metadata': {}, - 'shared_data': {}, - 'shared_scripts': {}, + "infer_tag": False, + "pure_python": True, + "dependencies": [], + "force_include_editable": {}, + "extra_metadata": {}, + "shared_data": {}, + "shared_scripts": {}, } def get_forced_inclusion_map(self, build_data: dict[str, Any]) -> dict[str, str]: - if not build_data['force_include_editable']: + if not build_data["force_include_editable"]: return self.config.get_force_include() - return normalize_inclusion_map(build_data['force_include_editable'], self.root) + return normalize_inclusion_map(build_data["force_include_editable"], self.root) @property def artifact_project_id(self) -> str: return ( self.project_id if self.config.strict_naming - else f'{self.normalize_file_name_component(self.metadata.core.raw_name)}-{self.metadata.version}' + else f"{self.normalize_file_name_component(self.metadata.core.raw_name)}-{self.metadata.version}" ) @classmethod diff --git a/backend/src/hatchling/cli/__init__.py b/backend/src/hatchling/cli/__init__.py index 6d2a22b99..8116effa8 100644 --- a/backend/src/hatchling/cli/__init__.py +++ b/backend/src/hatchling/cli/__init__.py @@ -7,10 +7,10 @@ def hatchling() -> int: - parser = argparse.ArgumentParser(prog='hatchling', allow_abbrev=False) + parser = argparse.ArgumentParser(prog="hatchling", allow_abbrev=False) subparsers = parser.add_subparsers() - defaults = {'metavar': ''} + defaults = {"metavar": ""} build_command(subparsers, defaults) dep_command(subparsers, defaults) @@ -19,7 +19,7 @@ def hatchling() -> int: kwargs = vars(parser.parse_args()) try: - command = kwargs.pop('func') + command = kwargs.pop("func") except KeyError: parser.print_help() else: diff --git a/backend/src/hatchling/cli/build/__init__.py b/backend/src/hatchling/cli/build/__init__.py index 1e4f1d35d..285a98bd4 100644 --- a/backend/src/hatchling/cli/build/__init__.py +++ b/backend/src/hatchling/cli/build/__init__.py @@ -26,7 +26,7 @@ def build_impl( app = Application() if hooks_only and no_hooks: - app.abort('Cannot use both --hooks-only and --no-hooks together') + app.abort("Cannot use both --hooks-only and --no-hooks together") root = os.getcwd() plugin_manager = PluginManager() @@ -35,12 +35,12 @@ def build_impl( target_data: dict[str, Any] = {} if targets: for data in targets: - target_name, _, version_data = data.partition(':') - versions = version_data.split(',') if version_data else [] + target_name, _, version_data = data.partition(":") + versions = version_data.split(",") if version_data else [] target_data.setdefault(target_name, []).extend(versions) else: # no cov - target_data['sdist'] = [] - target_data['wheel'] = [] + target_data["sdist"] = [] + target_data["wheel"] = [] builders = {} unknown_targets = [] @@ -58,7 +58,7 @@ def build_impl( root = os.getcwd() if no_hooks: - os.environ[BuildEnvVars.NO_HOOKS] = 'true' + os.environ[BuildEnvVars.NO_HOOKS] = "true" dynamic_dependencies: dict[str, None] = {} for i, (target_name, versions) in enumerate(target_data.items()): @@ -97,23 +97,23 @@ def build_impl( def build_command(subparsers: argparse._SubParsersAction, defaults: Any) -> None: - parser = subparsers.add_parser('build') + parser = subparsers.add_parser("build") parser.add_argument( - '-d', '--directory', dest='directory', help='The directory in which to build artifacts', **defaults + "-d", "--directory", dest="directory", help="The directory in which to build artifacts", **defaults ) parser.add_argument( - '-t', - '--target', - dest='targets', - action='append', - help='Comma-separated list of targets to build, overriding project defaults', + "-t", + "--target", + dest="targets", + action="append", + help="Comma-separated list of targets to build, overriding project defaults", **defaults, ) - parser.add_argument('--hooks-only', dest='hooks_only', action='store_true', default=None) - parser.add_argument('--no-hooks', dest='no_hooks', action='store_true', default=None) - parser.add_argument('-c', '--clean', dest='clean', action='store_true', default=None) - parser.add_argument('--clean-hooks-after', dest='clean_hooks_after', action='store_true', default=None) - parser.add_argument('--clean-only', dest='clean_only', action='store_true') - parser.add_argument('--show-dynamic-deps', dest='show_dynamic_deps', action='store_true') - parser.add_argument('--app', dest='called_by_app', action='store_true', help=argparse.SUPPRESS) + parser.add_argument("--hooks-only", dest="hooks_only", action="store_true", default=None) + parser.add_argument("--no-hooks", dest="no_hooks", action="store_true", default=None) + parser.add_argument("-c", "--clean", dest="clean", action="store_true", default=None) + parser.add_argument("--clean-hooks-after", dest="clean_hooks_after", action="store_true", default=None) + parser.add_argument("--clean-only", dest="clean_only", action="store_true") + parser.add_argument("--show-dynamic-deps", dest="show_dynamic_deps", action="store_true") + parser.add_argument("--app", dest="called_by_app", action="store_true", help=argparse.SUPPRESS) parser.set_defaults(func=build_impl) diff --git a/backend/src/hatchling/cli/dep/__init__.py b/backend/src/hatchling/cli/dep/__init__.py index 486850d80..5b83bb283 100644 --- a/backend/src/hatchling/cli/dep/__init__.py +++ b/backend/src/hatchling/cli/dep/__init__.py @@ -17,21 +17,21 @@ def synced_impl(*, dependencies: list[str], python: str) -> None: sys_path = None if python: - output = subprocess.check_output([python, '-c', 'import sys;print([path for path in sys.path if path])']) - sys_path = literal_eval(output.strip().decode('utf-8')) + output = subprocess.check_output([python, "-c", "import sys;print([path for path in sys.path if path])"]) + sys_path = literal_eval(output.strip().decode("utf-8")) sys.exit(0 if dependencies_in_sync(list(map(Requirement, dependencies)), sys_path) else 1) def synced_command(subparsers: argparse._SubParsersAction, defaults: Any) -> None: - parser = subparsers.add_parser('synced') - parser.add_argument('dependencies', nargs='+') - parser.add_argument('-p', '--python', dest='python', **defaults) + parser = subparsers.add_parser("synced") + parser.add_argument("dependencies", nargs="+") + parser.add_argument("-p", "--python", dest="python", **defaults) parser.set_defaults(func=synced_impl) def dep_command(subparsers: argparse._SubParsersAction, defaults: Any) -> None: - parser = subparsers.add_parser('dep') + parser = subparsers.add_parser("dep") subparsers = parser.add_subparsers() synced_command(subparsers, defaults) diff --git a/backend/src/hatchling/cli/dep/core.py b/backend/src/hatchling/cli/dep/core.py index 65101603d..4b360c291 100644 --- a/backend/src/hatchling/cli/dep/core.py +++ b/backend/src/hatchling/cli/dep/core.py @@ -13,10 +13,10 @@ def __init__(self, sys_path: list[str]) -> None: self._resolver = Distribution.discover(context=DistributionFinder.Context(path=sys_path)) self._distributions: dict[str, Distribution] = {} self._search_exhausted = False - self._canonical_regex = re.compile(r'[-_.]+') + self._canonical_regex = re.compile(r"[-_.]+") def __getitem__(self, item: str) -> Distribution | None: - item = self._canonical_regex.sub('-', item).lower() + item = self._canonical_regex.sub("-", item).lower() possible_distribution = self._distributions.get(item) if possible_distribution is not None: return possible_distribution @@ -27,11 +27,11 @@ def __getitem__(self, item: str) -> Distribution | None: return None for distribution in self._resolver: - name = distribution.metadata['Name'] + name = distribution.metadata["Name"] if name is None: continue - name = self._canonical_regex.sub('-', name).lower() + name = self._canonical_regex.sub("-", name).lower() self._distributions[name] = distribution if name == item: return distribution @@ -53,11 +53,11 @@ def dependency_in_sync( extras = requirement.extras if extras: - transitive_requirements: list[str] = distribution.metadata.get_all('Requires-Dist', []) + transitive_requirements: list[str] = distribution.metadata.get_all("Requires-Dist", []) if not transitive_requirements: return False - available_extras: list[str] = distribution.metadata.get_all('Provides-Extra', []) + available_extras: list[str] = distribution.metadata.get_all("Provides-Extra", []) for requirement_string in transitive_requirements: transitive_requirement = Requirement(requirement_string) @@ -71,7 +71,7 @@ def dependency_in_sync( return False extra_environment = dict(environment) - extra_environment['extra'] = extra + extra_environment["extra"] = extra if not dependency_in_sync(transitive_requirement, extra_environment, installed_distributions): return False @@ -80,30 +80,30 @@ def dependency_in_sync( # TODO: handle https://discuss.python.org/t/11938 if requirement.url: - direct_url_file = distribution.read_text('direct_url.json') + direct_url_file = distribution.read_text("direct_url.json") if direct_url_file is not None: import json # https://packaging.python.org/specifications/direct-url/ direct_url_data = json.loads(direct_url_file) - if 'vcs_info' in direct_url_data: - url = direct_url_data['url'] - vcs_info = direct_url_data['vcs_info'] - vcs = vcs_info['vcs'] - commit_id = vcs_info['commit_id'] - requested_revision = vcs_info.get('requested_revision') + if "vcs_info" in direct_url_data: + url = direct_url_data["url"] + vcs_info = direct_url_data["vcs_info"] + vcs = vcs_info["vcs"] + commit_id = vcs_info["commit_id"] + requested_revision = vcs_info.get("requested_revision") # Try a few variations, see https://peps.python.org/pep-0440/#direct-references if ( - requested_revision and requirement.url == f'{vcs}+{url}@{requested_revision}#{commit_id}' - ) or requirement.url == f'{vcs}+{url}@{commit_id}': + requested_revision and requirement.url == f"{vcs}+{url}@{requested_revision}#{commit_id}" + ) or requirement.url == f"{vcs}+{url}@{commit_id}": return True - if requirement.url in {f'{vcs}+{url}', f'{vcs}+{url}@{requested_revision}'}: + if requirement.url in {f"{vcs}+{url}", f"{vcs}+{url}@{requested_revision}"}: import subprocess - if vcs == 'git': - vcs_cmd = [vcs, 'ls-remote', url] + if vcs == "git": + vcs_cmd = [vcs, "ls-remote", url] if requested_revision: vcs_cmd.append(requested_revision) # TODO: add elifs for hg, svn, and bzr https://github.com/pypa/hatch/issues/760 diff --git a/backend/src/hatchling/cli/metadata/__init__.py b/backend/src/hatchling/cli/metadata/__init__.py index 34693394e..553d574d0 100644 --- a/backend/src/hatchling/cli/metadata/__init__.py +++ b/backend/src/hatchling/cli/metadata/__init__.py @@ -27,9 +27,9 @@ def metadata_impl( metadata = resolve_metadata_fields(project_metadata) if field: # no cov if field not in metadata: - app.abort(f'Unknown metadata field: {field}') - elif field == 'readme': - app.display(metadata[field]['text']) + app.abort(f"Unknown metadata field: {field}") + elif field == "readme": + app.display(metadata[field]["text"]) elif isinstance(metadata[field], str): app.display(metadata[field]) else: @@ -42,7 +42,7 @@ def metadata_impl( metadata.pop(key) if compact: - app.display(json.dumps(metadata, separators=(',', ':'))) + app.display(json.dumps(metadata, separators=(",", ":"))) else: # no cov app.display(json.dumps(metadata, indent=4)) @@ -51,8 +51,8 @@ def metadata_command( subparsers: argparse._SubParsersAction, defaults: Any, # noqa: ARG001 ) -> None: - parser = subparsers.add_parser('metadata') - parser.add_argument('field', nargs='?') - parser.add_argument('-c', '--compact', action='store_true') - parser.add_argument('--app', dest='called_by_app', action='store_true', help=argparse.SUPPRESS) + parser = subparsers.add_parser("metadata") + parser.add_argument("field", nargs="?") + parser.add_argument("-c", "--compact", action="store_true") + parser.add_argument("--app", dest="called_by_app", action="store_true", help=argparse.SUPPRESS) parser.set_defaults(func=metadata_impl) diff --git a/backend/src/hatchling/cli/version/__init__.py b/backend/src/hatchling/cli/version/__init__.py index 20cae309e..42aa0e29b 100644 --- a/backend/src/hatchling/cli/version/__init__.py +++ b/backend/src/hatchling/cli/version/__init__.py @@ -21,9 +21,9 @@ def version_impl( plugin_manager = PluginManager() metadata = ProjectMetadata(root, plugin_manager) - if 'version' in metadata.config.get('project', {}): + if "version" in metadata.config.get("project", {}): if desired_version: - app.abort('Cannot set version when it is statically defined by the `project.version` field') + app.abort("Cannot set version when it is statically defined by the `project.version` field") else: app.display(metadata.core.version) return @@ -31,7 +31,7 @@ def version_impl( source = metadata.hatch.version.source version_data = source.get_version_data() - original_version = version_data['version'] + original_version = version_data["version"] if not desired_version: app.display(original_version) @@ -40,12 +40,12 @@ def version_impl( updated_version = metadata.hatch.version.scheme.update(desired_version, original_version, version_data) source.set_version(updated_version, version_data) - app.display_info(f'Old: {original_version}') - app.display_info(f'New: {updated_version}') + app.display_info(f"Old: {original_version}") + app.display_info(f"New: {updated_version}") def version_command(subparsers: argparse._SubParsersAction, defaults: Any) -> None: - parser = subparsers.add_parser('version') - parser.add_argument('desired_version', default='', nargs='?', **defaults) - parser.add_argument('--app', dest='called_by_app', action='store_true', help=argparse.SUPPRESS) + parser = subparsers.add_parser("version") + parser.add_argument("desired_version", default="", nargs="?", **defaults) + parser.add_argument("--app", dest="called_by_app", action="store_true", help=argparse.SUPPRESS) parser.set_defaults(func=version_impl) diff --git a/backend/src/hatchling/metadata/core.py b/backend/src/hatchling/metadata/core.py index 88ed55775..063a3d027 100644 --- a/backend/src/hatchling/metadata/core.py +++ b/backend/src/hatchling/metadata/core.py @@ -32,7 +32,7 @@ def load_toml(path: str) -> dict[str, Any]: - with open(path, encoding='utf-8') as f: + with open(path, encoding="utf-8") as f: return tomllib.loads(f.read()) @@ -60,7 +60,7 @@ def __init__( # App already loaded config if config is not None and root is not None: - self._project_file = os.path.join(root, 'pyproject.toml') + self._project_file = os.path.join(root, "pyproject.toml") def has_project_file(self) -> bool: _ = self.config @@ -80,25 +80,25 @@ def context(self) -> Context: @property def core_raw_metadata(self) -> dict[str, Any]: if self._core_raw_metadata is None: - if 'project' not in self.config: - message = 'Missing `project` metadata table in configuration' + if "project" not in self.config: + message = "Missing `project` metadata table in configuration" raise ValueError(message) - core_raw_metadata = self.config['project'] + core_raw_metadata = self.config["project"] if not isinstance(core_raw_metadata, dict): - message = 'The `project` configuration must be a table' + message = "The `project` configuration must be a table" raise TypeError(message) core_raw_metadata = deepcopy(core_raw_metadata) - pkg_info = os.path.join(self.root, 'PKG-INFO') + pkg_info = os.path.join(self.root, "PKG-INFO") if os.path.isfile(pkg_info): from hatchling.metadata.spec import PROJECT_CORE_METADATA_FIELDS, project_metadata_from_core_metadata - with open(pkg_info, encoding='utf-8') as f: + with open(pkg_info, encoding="utf-8") as f: pkg_info_contents = f.read() base_metadata = project_metadata_from_core_metadata(pkg_info_contents) - defined_dynamic = core_raw_metadata.get('dynamic', []) + defined_dynamic = core_raw_metadata.get("dynamic", []) for field in list(defined_dynamic): if field in PROJECT_CORE_METADATA_FIELDS and field in base_metadata: core_raw_metadata[field] = base_metadata[field] @@ -112,14 +112,14 @@ def core_raw_metadata(self) -> dict[str, Any]: def dynamic(self) -> list[str]: # Keep track of the original dynamic fields before depopulation if self._dynamic is None: - dynamic = self.core_raw_metadata.get('dynamic', []) + dynamic = self.core_raw_metadata.get("dynamic", []) if not isinstance(dynamic, list): - message = 'Field `project.dynamic` must be an array' + message = "Field `project.dynamic` must be an array" raise TypeError(message) for i, field in enumerate(dynamic, 1): if not isinstance(field, str): - message = f'Field #{i} of field `project.dynamic` must be a string' + message = f"Field #{i} of field `project.dynamic` must be a string" raise TypeError(message) self._dynamic = list(dynamic) @@ -131,9 +131,9 @@ def name(self) -> str: # Duplicate the name parsing here for situations where it's # needed but metadata plugins might not be available if self._name is None: - name = self.core_raw_metadata.get('name', '') + name = self.core_raw_metadata.get("name", "") if not name: - message = 'Missing required field `project.name`' + message = "Missing required field `project.name`" raise ValueError(message) self._name = normalize_project_name(name) @@ -148,14 +148,14 @@ def version(self) -> str: if self._version is None: self._version = self._get_version() with suppress(ValueError): - self.core.dynamic.remove('version') + self.core.dynamic.remove("version") return self._version @property def config(self) -> dict[str, Any]: if self._config is None: - project_file = locate_file(self.root, 'pyproject.toml') + project_file = locate_file(self.root, "pyproject.toml") if project_file is None: self._config = {} else: @@ -167,9 +167,9 @@ def config(self) -> dict[str, Any]: @property def build(self) -> BuildMetadata: if self._build is None: - build_metadata = self.config.get('build-system', {}) + build_metadata = self.config.get("build-system", {}) if not isinstance(build_metadata, dict): - message = 'The `build-system` configuration must be a table' + message = "The `build-system` configuration must be a table" raise TypeError(message) self._build = BuildMetadata(self.root, build_metadata) @@ -187,9 +187,9 @@ def core(self) -> CoreMetadata: metadata_hooks = self.hatch.metadata.hooks if metadata_hooks: static_fields = set(self.core_raw_metadata) - if 'version' in self.hatch.config: + if "version" in self.hatch.config: self._version = self._get_version(metadata) - self.core_raw_metadata['version'] = self.version + self.core_raw_metadata["version"] = self.version if metadata.dynamic: for metadata_hook in metadata_hooks.values(): @@ -202,8 +202,8 @@ def core(self) -> CoreMetadata: metadata.dynamic.remove(new_field) else: message = ( - f'The field `{new_field}` was set dynamically and therefore must be ' - f'listed in `project.dynamic`' + f"The field `{new_field}` was set dynamically and therefore must be " + f"listed in `project.dynamic`" ) raise ValueError(message) @@ -214,20 +214,20 @@ def core(self) -> CoreMetadata: @property def hatch(self) -> HatchMetadata: if self._hatch is None: - tool_config = self.config.get('tool', {}) + tool_config = self.config.get("tool", {}) if not isinstance(tool_config, dict): - message = 'The `tool` configuration must be a table' + message = "The `tool` configuration must be a table" raise TypeError(message) - hatch_config = tool_config.get('hatch', {}) + hatch_config = tool_config.get("hatch", {}) if not isinstance(hatch_config, dict): - message = 'The `tool.hatch` configuration must be a table' + message = "The `tool.hatch` configuration must be a table" raise TypeError(message) hatch_file = ( os.path.join(os.path.dirname(self._project_file), DEFAULT_CONFIG_FILE) if self._project_file is not None - else locate_file(self.root, DEFAULT_CONFIG_FILE) or '' + else locate_file(self.root, DEFAULT_CONFIG_FILE) or "" ) if hatch_file and os.path.isfile(hatch_file): @@ -246,17 +246,17 @@ def _get_version(self, core_metadata: CoreMetadata | None = None) -> str: version = core_metadata.version if version is None: version = self.hatch.version.cached - source = f'source `{self.hatch.version.source_name}`' + source = f"source `{self.hatch.version.source_name}`" core_metadata._version_set = True # noqa: SLF001 else: - source = 'field `project.version`' + source = "field `project.version`" from packaging.version import InvalidVersion, Version try: normalized_version = str(Version(version)) except InvalidVersion: - message = f'Invalid version `{version}` from {source}, see https://peps.python.org/pep-0440/' + message = f"Invalid version `{version}` from {source}, see https://peps.python.org/pep-0440/" raise ValueError(message) from None else: return normalized_version @@ -285,22 +285,22 @@ def requires_complex(self) -> list[Requirement]: if self._requires_complex is None: from packaging.requirements import InvalidRequirement, Requirement - requires = self.config.get('requires', []) + requires = self.config.get("requires", []) if not isinstance(requires, list): - message = 'Field `build-system.requires` must be an array' + message = "Field `build-system.requires` must be an array" raise TypeError(message) requires_complex = [] for i, entry in enumerate(requires, 1): if not isinstance(entry, str): - message = f'Dependency #{i} of field `build-system.requires` must be a string' + message = f"Dependency #{i} of field `build-system.requires` must be a string" raise TypeError(message) try: requires_complex.append(Requirement(entry)) except InvalidRequirement as e: - message = f'Dependency #{i} of field `build-system.requires` is invalid: {e}' + message = f"Dependency #{i} of field `build-system.requires` is invalid: {e}" raise ValueError(message) from None self._requires_complex = requires_complex @@ -317,9 +317,9 @@ def requires(self) -> list[str]: @property def build_backend(self) -> str: if self._build_backend is None: - build_backend = self.config.get('build-backend', '') + build_backend = self.config.get("build-backend", "") if not isinstance(build_backend, str): - message = 'Field `build-system.build-backend` must be a string' + message = "Field `build-system.build-backend` must be a string" raise TypeError(message) self._build_backend = build_backend @@ -329,14 +329,14 @@ def build_backend(self) -> str: @property def backend_path(self) -> list[str]: if self._backend_path is None: - backend_path = self.config.get('backend-path', []) + backend_path = self.config.get("backend-path", []) if not isinstance(backend_path, list): - message = 'Field `build-system.backend-path` must be an array' + message = "Field `build-system.backend-path` must be an array" raise TypeError(message) for i, entry in enumerate(backend_path, 1): if not isinstance(entry, str): - message = f'Entry #{i} of field `build-system.backend-path` must be a string' + message = f"Entry #{i} of field `build-system.backend-path` must be a string" raise TypeError(message) self._backend_path = backend_path @@ -399,23 +399,23 @@ def raw_name(self) -> str: https://peps.python.org/pep-0621/#name """ if self._raw_name is None: - if 'name' in self.dynamic: - message = 'Static metadata field `name` cannot be present in field `project.dynamic`' + if "name" in self.dynamic: + message = "Static metadata field `name` cannot be present in field `project.dynamic`" raise ValueError(message) - raw_name = self.config.get('name', '') + raw_name = self.config.get("name", "") if not raw_name: - message = 'Missing required field `project.name`' + message = "Missing required field `project.name`" raise ValueError(message) if not isinstance(raw_name, str): - message = 'Field `project.name` must be a string' + message = "Field `project.name` must be a string" raise TypeError(message) if not is_valid_project_name(raw_name): message = ( - 'Required field `project.name` must only contain ASCII letters/digits, underscores, ' - 'hyphens, and periods, and must begin and end with ASCII letters/digits.' + "Required field `project.name` must only contain ASCII letters/digits, underscores, " + "hyphens, and periods, and must begin and end with ASCII letters/digits." ) raise ValueError(message) @@ -441,24 +441,24 @@ def version(self) -> str: version: str if self._version is None: - if 'version' not in self.config: - if not self._version_set and 'version' not in self.dynamic: + if "version" not in self.config: + if not self._version_set and "version" not in self.dynamic: message = ( - 'Field `project.version` can only be resolved dynamically ' - 'if `version` is in field `project.dynamic`' + "Field `project.version` can only be resolved dynamically " + "if `version` is in field `project.dynamic`" ) raise ValueError(message) else: - if 'version' in self.dynamic: + if "version" in self.dynamic: message = ( - 'Metadata field `version` cannot be both statically defined and ' - 'listed in field `project.dynamic`' + "Metadata field `version` cannot be both statically defined and " + "listed in field `project.dynamic`" ) raise ValueError(message) - version = self.config['version'] + version = self.config["version"] if not isinstance(version, str): - message = 'Field `project.version` must be a string' + message = "Field `project.version` must be a string" raise TypeError(message) self._version = version @@ -471,21 +471,21 @@ def description(self) -> str: https://peps.python.org/pep-0621/#description """ if self._description is None: - if 'description' in self.config: - description = self.config['description'] - if 'description' in self.dynamic: + if "description" in self.config: + description = self.config["description"] + if "description" in self.dynamic: message = ( - 'Metadata field `description` cannot be both statically defined and ' - 'listed in field `project.dynamic`' + "Metadata field `description` cannot be both statically defined and " + "listed in field `project.dynamic`" ) raise ValueError(message) else: - description = '' + description = "" if not isinstance(description, str): - message = 'Field `project.description` must be a string' + message = "Field `project.description` must be a string" raise TypeError(message) - self._description = ' '.join(description.splitlines()) + self._description = " ".join(description.splitlines()) return self._description @@ -498,95 +498,95 @@ def readme(self) -> str: content_type: str | None if self._readme is None: - if 'readme' in self.config: - readme = self.config['readme'] - if 'readme' in self.dynamic: + if "readme" in self.config: + readme = self.config["readme"] + if "readme" in self.dynamic: message = ( - 'Metadata field `readme` cannot be both statically defined and ' - 'listed in field `project.dynamic`' + "Metadata field `readme` cannot be both statically defined and " + "listed in field `project.dynamic`" ) raise ValueError(message) else: readme = None if readme is None: - self._readme = '' - self._readme_content_type = 'text/markdown' - self._readme_path = '' + self._readme = "" + self._readme_content_type = "text/markdown" + self._readme_path = "" elif isinstance(readme, str): normalized_path = readme.lower() - if normalized_path.endswith('.md'): - content_type = 'text/markdown' - elif normalized_path.endswith('.rst'): - content_type = 'text/x-rst' - elif normalized_path.endswith('.txt'): - content_type = 'text/plain' + if normalized_path.endswith(".md"): + content_type = "text/markdown" + elif normalized_path.endswith(".rst"): + content_type = "text/x-rst" + elif normalized_path.endswith(".txt"): + content_type = "text/plain" else: - message = f'Unable to determine the content-type based on the extension of readme file: {readme}' + message = f"Unable to determine the content-type based on the extension of readme file: {readme}" raise TypeError(message) readme_path = os.path.normpath(os.path.join(self.root, readme)) if not os.path.isfile(readme_path): - message = f'Readme file does not exist: {readme}' + message = f"Readme file does not exist: {readme}" raise OSError(message) - with open(readme_path, encoding='utf-8') as f: + with open(readme_path, encoding="utf-8") as f: self._readme = f.read() self._readme_content_type = content_type self._readme_path = readme elif isinstance(readme, dict): - content_type = readme.get('content-type') + content_type = readme.get("content-type") if content_type is None: - message = 'Field `content-type` is required in the `project.readme` table' + message = "Field `content-type` is required in the `project.readme` table" raise ValueError(message) if not isinstance(content_type, str): - message = 'Field `content-type` in the `project.readme` table must be a string' + message = "Field `content-type` in the `project.readme` table must be a string" raise TypeError(message) - if content_type not in {'text/markdown', 'text/x-rst', 'text/plain'}: + if content_type not in {"text/markdown", "text/x-rst", "text/plain"}: message = ( - 'Field `content-type` in the `project.readme` table must be one of the following: ' - 'text/markdown, text/x-rst, text/plain' + "Field `content-type` in the `project.readme` table must be one of the following: " + "text/markdown, text/x-rst, text/plain" ) raise ValueError(message) - if 'file' in readme and 'text' in readme: - message = 'Cannot specify both `file` and `text` in the `project.readme` table' + if "file" in readme and "text" in readme: + message = "Cannot specify both `file` and `text` in the `project.readme` table" raise ValueError(message) - if 'file' in readme: - relative_path = readme['file'] + if "file" in readme: + relative_path = readme["file"] if not isinstance(relative_path, str): - message = 'Field `file` in the `project.readme` table must be a string' + message = "Field `file` in the `project.readme` table must be a string" raise TypeError(message) path = os.path.normpath(os.path.join(self.root, relative_path)) if not os.path.isfile(path): - message = f'Readme file does not exist: {relative_path}' + message = f"Readme file does not exist: {relative_path}" raise OSError(message) - with open(path, encoding=readme.get('charset', 'utf-8')) as f: + with open(path, encoding=readme.get("charset", "utf-8")) as f: contents = f.read() readme_path = relative_path - elif 'text' in readme: - contents = readme['text'] + elif "text" in readme: + contents = readme["text"] if not isinstance(contents, str): - message = 'Field `text` in the `project.readme` table must be a string' + message = "Field `text` in the `project.readme` table must be a string" raise TypeError(message) - readme_path = '' + readme_path = "" else: - message = 'Must specify either `file` or `text` in the `project.readme` table' + message = "Must specify either `file` or `text` in the `project.readme` table" raise ValueError(message) self._readme = contents self._readme_content_type = content_type self._readme_path = readme_path else: - message = 'Field `project.readme` must be a string or a table' + message = "Field `project.readme` must be a string or a table" raise TypeError(message) return self._readme @@ -619,25 +619,25 @@ def requires_python(self) -> str: if self._requires_python is None: from packaging.specifiers import InvalidSpecifier, SpecifierSet - if 'requires-python' in self.config: - requires_python = self.config['requires-python'] - if 'requires-python' in self.dynamic: + if "requires-python" in self.config: + requires_python = self.config["requires-python"] + if "requires-python" in self.dynamic: message = ( - 'Metadata field `requires-python` cannot be both statically defined and ' - 'listed in field `project.dynamic`' + "Metadata field `requires-python` cannot be both statically defined and " + "listed in field `project.dynamic`" ) raise ValueError(message) else: - requires_python = '' + requires_python = "" if not isinstance(requires_python, str): - message = 'Field `project.requires-python` must be a string' + message = "Field `project.requires-python` must be a string" raise TypeError(message) try: self._python_constraint = SpecifierSet(requires_python) except InvalidSpecifier as e: - message = f'Field `project.requires-python` is invalid: {e}' + message = f"Field `project.requires-python` is invalid: {e}" raise ValueError(message) from None self._requires_python = str(self._python_constraint) @@ -659,61 +659,61 @@ def license(self) -> str: https://peps.python.org/pep-0621/#license """ if self._license is None: - if 'license' in self.config: - data = self.config['license'] - if 'license' in self.dynamic: + if "license" in self.config: + data = self.config["license"] + if "license" in self.dynamic: message = ( - 'Metadata field `license` cannot be both statically defined and ' - 'listed in field `project.dynamic`' + "Metadata field `license` cannot be both statically defined and " + "listed in field `project.dynamic`" ) raise ValueError(message) else: data = None if data is None: - self._license = '' - self._license_expression = '' + self._license = "" + self._license_expression = "" elif isinstance(data, str): from packaging.licenses import canonicalize_license_expression try: self._license_expression = str(canonicalize_license_expression(data)) except ValueError as e: - message = f'Error parsing field `project.license` - {e}' + message = f"Error parsing field `project.license` - {e}" raise ValueError(message) from None - self._license = '' + self._license = "" elif isinstance(data, dict): - if 'file' in data and 'text' in data: - message = 'Cannot specify both `file` and `text` in the `project.license` table' + if "file" in data and "text" in data: + message = "Cannot specify both `file` and `text` in the `project.license` table" raise ValueError(message) - if 'file' in data: - relative_path = data['file'] + if "file" in data: + relative_path = data["file"] if not isinstance(relative_path, str): - message = 'Field `file` in the `project.license` table must be a string' + message = "Field `file` in the `project.license` table must be a string" raise TypeError(message) path = os.path.normpath(os.path.join(self.root, relative_path)) if not os.path.isfile(path): - message = f'License file does not exist: {relative_path}' + message = f"License file does not exist: {relative_path}" raise OSError(message) - with open(path, encoding='utf-8') as f: + with open(path, encoding="utf-8") as f: contents = f.read() - elif 'text' in data: - contents = data['text'] + elif "text" in data: + contents = data["text"] if not isinstance(contents, str): - message = 'Field `text` in the `project.license` table must be a string' + message = "Field `text` in the `project.license` table must be a string" raise TypeError(message) else: - message = 'Must specify either `file` or `text` in the `project.license` table' + message = "Must specify either `file` or `text` in the `project.license` table" raise ValueError(message) self._license = contents - self._license_expression = '' + self._license_expression = "" else: - message = 'Field `project.license` must be a string or a table' + message = "Field `project.license` must be a string or a table" raise TypeError(message) return self._license @@ -734,35 +734,35 @@ def license_files(self) -> list[str]: https://peps.python.org/pep-0639/ """ if self._license_files is None: - if 'license-files' in self.config: - globs = self.config['license-files'] - if 'license-files' in self.dynamic: + if "license-files" in self.config: + globs = self.config["license-files"] + if "license-files" in self.dynamic: message = ( - 'Metadata field `license-files` cannot be both statically defined and ' - 'listed in field `project.dynamic`' + "Metadata field `license-files` cannot be both statically defined and " + "listed in field `project.dynamic`" ) raise ValueError(message) if isinstance(globs, dict): - globs = globs.get('globs', globs.get('paths', [])) + globs = globs.get("globs", globs.get("paths", [])) else: - globs = ['LICEN[CS]E*', 'COPYING*', 'NOTICE*', 'AUTHORS*'] + globs = ["LICEN[CS]E*", "COPYING*", "NOTICE*", "AUTHORS*"] from glob import glob license_files: list[str] = [] if not isinstance(globs, list): - message = 'Field `project.license-files` must be an array' + message = "Field `project.license-files` must be an array" raise TypeError(message) for i, pattern in enumerate(globs, 1): if not isinstance(pattern, str): - message = f'Entry #{i} of field `project.license-files` must be a string' + message = f"Entry #{i} of field `project.license-files` must be a string" raise TypeError(message) full_pattern = os.path.normpath(os.path.join(self.root, pattern)) license_files.extend( - os.path.relpath(path, self.root).replace('\\', '/') + os.path.relpath(path, self.root).replace("\\", "/") for path in glob(full_pattern) if os.path.isfile(path) ) @@ -780,49 +780,49 @@ def authors(self) -> list[str]: authors_data: dict[str, list[str]] if self._authors is None: - if 'authors' in self.config: - authors = self.config['authors'] - if 'authors' in self.dynamic: + if "authors" in self.config: + authors = self.config["authors"] + if "authors" in self.dynamic: message = ( - 'Metadata field `authors` cannot be both statically defined and ' - 'listed in field `project.dynamic`' + "Metadata field `authors` cannot be both statically defined and " + "listed in field `project.dynamic`" ) raise ValueError(message) else: authors = [] if not isinstance(authors, list): - message = 'Field `project.authors` must be an array' + message = "Field `project.authors` must be an array" raise TypeError(message) from email.headerregistry import Address authors = deepcopy(authors) - authors_data = {'name': [], 'email': []} + authors_data = {"name": [], "email": []} for i, data in enumerate(authors, 1): if not isinstance(data, dict): - message = f'Author #{i} of field `project.authors` must be an inline table' + message = f"Author #{i} of field `project.authors` must be an inline table" raise TypeError(message) - name = data.get('name', '') + name = data.get("name", "") if not isinstance(name, str): - message = f'Name of author #{i} of field `project.authors` must be a string' + message = f"Name of author #{i} of field `project.authors` must be a string" raise TypeError(message) - email = data.get('email', '') + email = data.get("email", "") if not isinstance(email, str): - message = f'Email of author #{i} of field `project.authors` must be a string' + message = f"Email of author #{i} of field `project.authors` must be a string" raise TypeError(message) if name and email: - authors_data['email'].append(str(Address(display_name=name, addr_spec=email))) + authors_data["email"].append(str(Address(display_name=name, addr_spec=email))) elif email: - authors_data['email'].append(str(Address(addr_spec=email))) + authors_data["email"].append(str(Address(addr_spec=email))) elif name: - authors_data['name'].append(name) + authors_data["name"].append(name) else: - message = f'Author #{i} of field `project.authors` must specify either `name` or `email`' + message = f"Author #{i} of field `project.authors` must specify either `name` or `email`" raise ValueError(message) self._authors = authors @@ -848,49 +848,49 @@ def maintainers(self) -> list[str]: maintainers: list[str] if self._maintainers is None: - if 'maintainers' in self.config: - maintainers = self.config['maintainers'] - if 'maintainers' in self.dynamic: + if "maintainers" in self.config: + maintainers = self.config["maintainers"] + if "maintainers" in self.dynamic: message = ( - 'Metadata field `maintainers` cannot be both statically defined and ' - 'listed in field `project.dynamic`' + "Metadata field `maintainers` cannot be both statically defined and " + "listed in field `project.dynamic`" ) raise ValueError(message) else: maintainers = [] if not isinstance(maintainers, list): - message = 'Field `project.maintainers` must be an array' + message = "Field `project.maintainers` must be an array" raise TypeError(message) from email.headerregistry import Address maintainers = deepcopy(maintainers) - maintainers_data: dict[str, list[str]] = {'name': [], 'email': []} + maintainers_data: dict[str, list[str]] = {"name": [], "email": []} for i, data in enumerate(maintainers, 1): if not isinstance(data, dict): - message = f'Maintainer #{i} of field `project.maintainers` must be an inline table' + message = f"Maintainer #{i} of field `project.maintainers` must be an inline table" raise TypeError(message) - name = data.get('name', '') + name = data.get("name", "") if not isinstance(name, str): - message = f'Name of maintainer #{i} of field `project.maintainers` must be a string' + message = f"Name of maintainer #{i} of field `project.maintainers` must be a string" raise TypeError(message) - email = data.get('email', '') + email = data.get("email", "") if not isinstance(email, str): - message = f'Email of maintainer #{i} of field `project.maintainers` must be a string' + message = f"Email of maintainer #{i} of field `project.maintainers` must be a string" raise TypeError(message) if name and email: - maintainers_data['email'].append(str(Address(display_name=name, addr_spec=email))) + maintainers_data["email"].append(str(Address(display_name=name, addr_spec=email))) elif email: - maintainers_data['email'].append(str(Address(addr_spec=email))) + maintainers_data["email"].append(str(Address(addr_spec=email))) elif name: - maintainers_data['name'].append(name) + maintainers_data["name"].append(name) else: - message = f'Maintainer #{i} of field `project.maintainers` must specify either `name` or `email`' + message = f"Maintainer #{i} of field `project.maintainers` must specify either `name` or `email`" raise ValueError(message) self._maintainers = maintainers @@ -914,26 +914,26 @@ def keywords(self) -> list[str]: https://peps.python.org/pep-0621/#keywords """ if self._keywords is None: - if 'keywords' in self.config: - keywords = self.config['keywords'] - if 'keywords' in self.dynamic: + if "keywords" in self.config: + keywords = self.config["keywords"] + if "keywords" in self.dynamic: message = ( - 'Metadata field `keywords` cannot be both statically defined and ' - 'listed in field `project.dynamic`' + "Metadata field `keywords` cannot be both statically defined and " + "listed in field `project.dynamic`" ) raise ValueError(message) else: keywords = [] if not isinstance(keywords, list): - message = 'Field `project.keywords` must be an array' + message = "Field `project.keywords` must be an array" raise TypeError(message) unique_keywords = set() for i, keyword in enumerate(keywords, 1): if not isinstance(keyword, str): - message = f'Keyword #{i} of field `project.keywords` must be a string' + message = f"Keyword #{i} of field `project.keywords` must be a string" raise TypeError(message) unique_keywords.add(keyword) @@ -950,22 +950,22 @@ def classifiers(self) -> list[str]: if self._classifiers is None: import bisect - if 'classifiers' in self.config: - classifiers = self.config['classifiers'] - if 'classifiers' in self.dynamic: + if "classifiers" in self.config: + classifiers = self.config["classifiers"] + if "classifiers" in self.dynamic: message = ( - 'Metadata field `classifiers` cannot be both statically defined and ' - 'listed in field `project.dynamic`' + "Metadata field `classifiers` cannot be both statically defined and " + "listed in field `project.dynamic`" ) raise ValueError(message) else: classifiers = [] if not isinstance(classifiers, list): - message = 'Field `project.classifiers` must be an array' + message = "Field `project.classifiers` must be an array" raise TypeError(message) - verify_classifiers = not os.environ.get('HATCH_METADATA_CLASSIFIERS_NO_VERIFY') + verify_classifiers = not os.environ.get("HATCH_METADATA_CLASSIFIERS_NO_VERIFY") if verify_classifiers: import trove_classifiers @@ -979,7 +979,7 @@ def classifiers(self) -> list[str]: for i, classifier in enumerate(classifiers, 1): if not isinstance(classifier, str): - message = f'Classifier #{i} of field `project.classifiers` must be a string' + message = f"Classifier #{i} of field `project.classifiers` must be a string" raise TypeError(message) if ( @@ -987,7 +987,7 @@ def classifiers(self) -> list[str]: and verify_classifiers and classifier not in known_classifiers ): - message = f'Unknown classifier in field `project.classifiers`: {classifier}' + message = f"Unknown classifier in field `project.classifiers`: {classifier}" raise ValueError(message) unique_classifiers.add(classifier) @@ -996,7 +996,7 @@ def classifiers(self) -> list[str]: import re # combined text-numeric sort that ensures that Python versions sort correctly - split_re = re.compile(r'(\D*)(\d*)') + split_re = re.compile(r"(\D*)(\d*)") sorted_classifiers = sorted( classifiers, key=lambda value: ([(a, int(b) if b else None) for a, b in split_re.findall(value)]), @@ -1014,25 +1014,25 @@ def urls(self) -> dict[str, str]: https://peps.python.org/pep-0621/#urls """ if self._urls is None: - if 'urls' in self.config: - urls = self.config['urls'] - if 'urls' in self.dynamic: + if "urls" in self.config: + urls = self.config["urls"] + if "urls" in self.dynamic: message = ( - 'Metadata field `urls` cannot be both statically defined and listed in field `project.dynamic`' + "Metadata field `urls` cannot be both statically defined and listed in field `project.dynamic`" ) raise ValueError(message) else: urls = {} if not isinstance(urls, dict): - message = 'Field `project.urls` must be a table' + message = "Field `project.urls` must be a table" raise TypeError(message) sorted_urls = {} for label, url in urls.items(): if not isinstance(url, str): - message = f'URL `{label}` of field `project.urls` must be a string' + message = f"URL `{label}` of field `project.urls` must be a string" raise TypeError(message) sorted_urls[label] = url @@ -1047,26 +1047,26 @@ def scripts(self) -> dict[str, str]: https://peps.python.org/pep-0621/#entry-points """ if self._scripts is None: - if 'scripts' in self.config: - scripts = self.config['scripts'] - if 'scripts' in self.dynamic: + if "scripts" in self.config: + scripts = self.config["scripts"] + if "scripts" in self.dynamic: message = ( - 'Metadata field `scripts` cannot be both statically defined and ' - 'listed in field `project.dynamic`' + "Metadata field `scripts` cannot be both statically defined and " + "listed in field `project.dynamic`" ) raise ValueError(message) else: scripts = {} if not isinstance(scripts, dict): - message = 'Field `project.scripts` must be a table' + message = "Field `project.scripts` must be a table" raise TypeError(message) sorted_scripts = {} for name, object_ref in sorted(scripts.items()): if not isinstance(object_ref, str): - message = f'Object reference `{name}` of field `project.scripts` must be a string' + message = f"Object reference `{name}` of field `project.scripts` must be a string" raise TypeError(message) sorted_scripts[name] = object_ref @@ -1081,26 +1081,26 @@ def gui_scripts(self) -> dict[str, str]: https://peps.python.org/pep-0621/#entry-points """ if self._gui_scripts is None: - if 'gui-scripts' in self.config: - gui_scripts = self.config['gui-scripts'] - if 'gui-scripts' in self.dynamic: + if "gui-scripts" in self.config: + gui_scripts = self.config["gui-scripts"] + if "gui-scripts" in self.dynamic: message = ( - 'Metadata field `gui-scripts` cannot be both statically defined and ' - 'listed in field `project.dynamic`' + "Metadata field `gui-scripts` cannot be both statically defined and " + "listed in field `project.dynamic`" ) raise ValueError(message) else: gui_scripts = {} if not isinstance(gui_scripts, dict): - message = 'Field `project.gui-scripts` must be a table' + message = "Field `project.gui-scripts` must be a table" raise TypeError(message) sorted_gui_scripts = {} for name, object_ref in sorted(gui_scripts.items()): if not isinstance(object_ref, str): - message = f'Object reference `{name}` of field `project.gui-scripts` must be a string' + message = f"Object reference `{name}` of field `project.gui-scripts` must be a string" raise TypeError(message) sorted_gui_scripts[name] = object_ref @@ -1115,26 +1115,26 @@ def entry_points(self) -> dict[str, dict[str, str]]: https://peps.python.org/pep-0621/#entry-points """ if self._entry_points is None: - if 'entry-points' in self.config: - defined_entry_point_groups = self.config['entry-points'] - if 'entry-points' in self.dynamic: + if "entry-points" in self.config: + defined_entry_point_groups = self.config["entry-points"] + if "entry-points" in self.dynamic: message = ( - 'Metadata field `entry-points` cannot be both statically defined and ' - 'listed in field `project.dynamic`' + "Metadata field `entry-points` cannot be both statically defined and " + "listed in field `project.dynamic`" ) raise ValueError(message) else: defined_entry_point_groups = {} if not isinstance(defined_entry_point_groups, dict): - message = 'Field `project.entry-points` must be a table' + message = "Field `project.entry-points` must be a table" raise TypeError(message) - for forbidden_field, expected_field in (('console_scripts', 'scripts'), ('gui-scripts', 'gui-scripts')): + for forbidden_field, expected_field in (("console_scripts", "scripts"), ("gui-scripts", "gui-scripts")): if forbidden_field in defined_entry_point_groups: message = ( - f'Field `{forbidden_field}` must be defined as `project.{expected_field}` ' - f'instead of in the `project.entry-points` table' + f"Field `{forbidden_field}` must be defined as `project.{expected_field}` " + f"instead of in the `project.entry-points` table" ) raise ValueError(message) @@ -1142,14 +1142,14 @@ def entry_points(self) -> dict[str, dict[str, str]]: for group, entry_point_data in sorted(defined_entry_point_groups.items()): if not isinstance(entry_point_data, dict): - message = f'Field `project.entry-points.{group}` must be a table' + message = f"Field `project.entry-points.{group}` must be a table" raise TypeError(message) entry_points = {} for name, object_ref in sorted(entry_point_data.items()): if not isinstance(object_ref, str): - message = f'Object reference `{name}` of field `project.entry-points.{group}` must be a string' + message = f"Object reference `{name}` of field `project.entry-points.{group}` must be a string" raise TypeError(message) entry_points[name] = object_ref @@ -1169,38 +1169,38 @@ def dependencies_complex(self) -> dict[str, Requirement]: if self._dependencies_complex is None: from packaging.requirements import InvalidRequirement, Requirement - if 'dependencies' in self.config: - dependencies = self.config['dependencies'] - if 'dependencies' in self.dynamic: + if "dependencies" in self.config: + dependencies = self.config["dependencies"] + if "dependencies" in self.dynamic: message = ( - 'Metadata field `dependencies` cannot be both statically defined and ' - 'listed in field `project.dynamic`' + "Metadata field `dependencies` cannot be both statically defined and " + "listed in field `project.dynamic`" ) raise ValueError(message) else: dependencies = [] if not isinstance(dependencies, list): - message = 'Field `project.dependencies` must be an array' + message = "Field `project.dependencies` must be an array" raise TypeError(message) dependencies_complex = {} for i, entry in enumerate(dependencies, 1): if not isinstance(entry, str): - message = f'Dependency #{i} of field `project.dependencies` must be a string' + message = f"Dependency #{i} of field `project.dependencies` must be a string" raise TypeError(message) try: requirement = Requirement(self.context.format(entry)) except InvalidRequirement as e: - message = f'Dependency #{i} of field `project.dependencies` is invalid: {e}' + message = f"Dependency #{i} of field `project.dependencies` is invalid: {e}" raise ValueError(message) from None else: if requirement.url and not self.hatch_metadata.allow_direct_references: message = ( - f'Dependency #{i} of field `project.dependencies` cannot be a direct reference unless ' - f'field `tool.hatch.metadata.allow-direct-references` is set to `true`' + f"Dependency #{i} of field `project.dependencies` cannot be a direct reference unless " + f"field `tool.hatch.metadata.allow-direct-references` is set to `true`" ) raise ValueError(message) @@ -1229,19 +1229,19 @@ def optional_dependencies_complex(self) -> dict[str, dict[str, Requirement]]: if self._optional_dependencies_complex is None: from packaging.requirements import InvalidRequirement, Requirement - if 'optional-dependencies' in self.config: - optional_dependencies = self.config['optional-dependencies'] - if 'optional-dependencies' in self.dynamic: + if "optional-dependencies" in self.config: + optional_dependencies = self.config["optional-dependencies"] + if "optional-dependencies" in self.dynamic: message = ( - 'Metadata field `optional-dependencies` cannot be both statically defined and ' - 'listed in field `project.dynamic`' + "Metadata field `optional-dependencies` cannot be both statically defined and " + "listed in field `project.dynamic`" ) raise ValueError(message) else: optional_dependencies = {} if not isinstance(optional_dependencies, dict): - message = 'Field `project.optional-dependencies` must be a table' + message = "Field `project.optional-dependencies` must be a table" raise TypeError(message) normalized_options: dict[str, str] = {} @@ -1251,9 +1251,9 @@ def optional_dependencies_complex(self) -> dict[str, dict[str, Requirement]]: for option, dependencies in optional_dependencies.items(): if not is_valid_project_name(option): message = ( - f'Optional dependency group `{option}` of field `project.optional-dependencies` must only ' - f'contain ASCII letters/digits, underscores, hyphens, and periods, and must begin and end with ' - f'ASCII letters/digits.' + f"Optional dependency group `{option}` of field `project.optional-dependencies` must only " + f"contain ASCII letters/digits, underscores, hyphens, and periods, and must begin and end with " + f"ASCII letters/digits." ) raise ValueError(message) @@ -1262,14 +1262,14 @@ def optional_dependencies_complex(self) -> dict[str, dict[str, Requirement]]: ) if normalized_option in normalized_options: message = ( - f'Optional dependency groups `{normalized_options[normalized_option]}` and `{option}` of ' - f'field `project.optional-dependencies` both evaluate to `{normalized_option}`.' + f"Optional dependency groups `{normalized_options[normalized_option]}` and `{option}` of " + f"field `project.optional-dependencies` both evaluate to `{normalized_option}`." ) raise ValueError(message) if not isinstance(dependencies, list): message = ( - f'Dependencies for option `{option}` of field `project.optional-dependencies` must be an array' + f"Dependencies for option `{option}` of field `project.optional-dependencies` must be an array" ) raise TypeError(message) @@ -1278,8 +1278,8 @@ def optional_dependencies_complex(self) -> dict[str, dict[str, Requirement]]: for i, entry in enumerate(dependencies, 1): if not isinstance(entry, str): message = ( - f'Dependency #{i} of option `{option}` of field `project.optional-dependencies` ' - f'must be a string' + f"Dependency #{i} of option `{option}` of field `project.optional-dependencies` " + f"must be a string" ) raise TypeError(message) @@ -1287,16 +1287,16 @@ def optional_dependencies_complex(self) -> dict[str, dict[str, Requirement]]: requirement = Requirement(self.context.format(entry)) except InvalidRequirement as e: message = ( - f'Dependency #{i} of option `{option}` of field `project.optional-dependencies` ' - f'is invalid: {e}' + f"Dependency #{i} of option `{option}` of field `project.optional-dependencies` " + f"is invalid: {e}" ) raise ValueError(message) from None else: if requirement.url and not self.hatch_metadata.allow_direct_references: message = ( - f'Dependency #{i} of option `{option}` of field `project.optional-dependencies` ' - f'cannot be a direct reference unless field ' - f'`tool.hatch.metadata.allow-direct-references` is set to `true`' + f"Dependency #{i} of option `{option}` of field `project.optional-dependencies` " + f"cannot be a direct reference unless field " + f"`tool.hatch.metadata.allow-direct-references` is set to `true`" ) raise ValueError(message) @@ -1343,14 +1343,14 @@ def dynamic(self) -> list[str]: https://peps.python.org/pep-0621/#dynamic """ if self._dynamic is None: - dynamic = self.config.get('dynamic', []) + dynamic = self.config.get("dynamic", []) if not isinstance(dynamic, list): - message = 'Field `project.dynamic` must be an array' + message = "Field `project.dynamic` must be an array" raise TypeError(message) if not all(isinstance(entry, str) for entry in dynamic): - message = 'Field `project.dynamic` must only contain strings' + message = "Field `project.dynamic` must only contain strings" raise TypeError(message) self._dynamic = sorted(dynamic) @@ -1367,7 +1367,7 @@ def validate_fields(self) -> None: @staticmethod def __classifier_is_private(classifier: str) -> bool: - return classifier.lower().startswith('private ::') + return classifier.lower().startswith("private ::") class HatchMetadata(Generic[PluginManagerBound]): @@ -1384,9 +1384,9 @@ def __init__(self, root: str, config: dict[str, dict[str, Any]], plugin_manager: @property def metadata(self) -> HatchMetadataSettings: if self._metadata is None: - metadata_config = self.config.get('metadata', {}) + metadata_config = self.config.get("metadata", {}) if not isinstance(metadata_config, dict): - message = 'Field `tool.hatch.metadata` must be a table' + message = "Field `tool.hatch.metadata` must be a table" raise TypeError(message) self._metadata = HatchMetadataSettings(self.root, metadata_config, self.plugin_manager) @@ -1396,9 +1396,9 @@ def metadata(self) -> HatchMetadataSettings: @property def build_config(self) -> dict[str, Any]: if self._build_config is None: - build_config = self.config.get('build', {}) + build_config = self.config.get("build", {}) if not isinstance(build_config, dict): - message = 'Field `tool.hatch.build` must be a table' + message = "Field `tool.hatch.build` must be a table" raise TypeError(message) self._build_config = build_config @@ -1408,9 +1408,9 @@ def build_config(self) -> dict[str, Any]: @property def build_targets(self) -> dict[str, Any]: if self._build_targets is None: - build_targets: dict = self.build_config.get('targets', {}) + build_targets: dict = self.build_config.get("targets", {}) if not isinstance(build_targets, dict): - message = 'Field `tool.hatch.build.targets` must be a table' + message = "Field `tool.hatch.build.targets` must be a table" raise TypeError(message) self._build_targets = build_targets @@ -1420,13 +1420,13 @@ def build_targets(self) -> dict[str, Any]: @property def version(self) -> HatchVersionConfig: if self._version is None: - if 'version' not in self.config: - message = 'Missing `tool.hatch.version` configuration' + if "version" not in self.config: + message = "Missing `tool.hatch.version` configuration" raise ValueError(message) - options = self.config['version'] + options = self.config["version"] if not isinstance(options, dict): - message = 'Field `tool.hatch.version` must be a table' + message = "Field `tool.hatch.version` must be a table" raise TypeError(message) self._version = HatchVersionConfig(self.root, deepcopy(options), self.plugin_manager) @@ -1450,9 +1450,9 @@ def __init__(self, root: str, config: dict[str, Any], plugin_manager: PluginMana def cached(self) -> str: if self._cached is None: try: - self._cached = self.source.get_version_data()['version'] + self._cached = self.source.get_version_data()["version"] except Exception as e: # noqa: BLE001 - message = f'Error getting the version from source `{self.source.PLUGIN_NAME}`: {e}' + message = f"Error getting the version from source `{self.source.PLUGIN_NAME}`: {e}" raise type(e)(message) from None return self._cached @@ -1460,13 +1460,13 @@ def cached(self) -> str: @property def source_name(self) -> str: if self._source_name is None: - source: str = self.config.get('source', 'regex') + source: str = self.config.get("source", "regex") if not source: - message = 'The `source` option under the `tool.hatch.version` table must not be empty if defined' + message = "The `source` option under the `tool.hatch.version` table must not be empty if defined" raise ValueError(message) if not isinstance(source, str): - message = 'Field `tool.hatch.version.source` must be a string' + message = "Field `tool.hatch.version.source` must be a string" raise TypeError(message) self._source_name = source @@ -1476,13 +1476,13 @@ def source_name(self) -> str: @property def scheme_name(self) -> str: if self._scheme_name is None: - scheme: str = self.config.get('scheme', 'standard') + scheme: str = self.config.get("scheme", "standard") if not scheme: - message = 'The `scheme` option under the `tool.hatch.version` table must not be empty if defined' + message = "The `scheme` option under the `tool.hatch.version` table must not be empty if defined" raise ValueError(message) if not isinstance(scheme, str): - message = 'Field `tool.hatch.version.scheme` must be a string' + message = "Field `tool.hatch.version.scheme` must be a string" raise TypeError(message) self._scheme_name = scheme @@ -1499,7 +1499,7 @@ def source(self) -> VersionSourceInterface: if version_source is None: from hatchling.plugin.exceptions import UnknownPluginError - message = f'Unknown version source: {source_name}' + message = f"Unknown version source: {source_name}" raise UnknownPluginError(message) self._source = version_source(self.root, deepcopy(self.config)) @@ -1516,7 +1516,7 @@ def scheme(self) -> VersionSchemeInterface: if version_scheme is None: from hatchling.plugin.exceptions import UnknownPluginError - message = f'Unknown version scheme: {scheme_name}' + message = f"Unknown version scheme: {scheme_name}" raise UnknownPluginError(message) self._scheme = version_scheme(self.root, deepcopy(self.config)) @@ -1538,9 +1538,9 @@ def __init__(self, root: str, config: dict[str, Any], plugin_manager: PluginMana @property def allow_direct_references(self) -> bool: if self._allow_direct_references is None: - allow_direct_references: bool = self.config.get('allow-direct-references', False) + allow_direct_references: bool = self.config.get("allow-direct-references", False) if not isinstance(allow_direct_references, bool): - message = 'Field `tool.hatch.metadata.allow-direct-references` must be a boolean' + message = "Field `tool.hatch.metadata.allow-direct-references` must be a boolean" raise TypeError(message) self._allow_direct_references = allow_direct_references @@ -1551,9 +1551,9 @@ def allow_direct_references(self) -> bool: def allow_ambiguous_features(self) -> bool: # TODO: remove in the first minor release after Jan 1, 2024 if self._allow_ambiguous_features is None: - allow_ambiguous_features: bool = self.config.get('allow-ambiguous-features', False) + allow_ambiguous_features: bool = self.config.get("allow-ambiguous-features", False) if not isinstance(allow_ambiguous_features, bool): - message = 'Field `tool.hatch.metadata.allow-ambiguous-features` must be a boolean' + message = "Field `tool.hatch.metadata.allow-ambiguous-features` must be a boolean" raise TypeError(message) self._allow_ambiguous_features = allow_ambiguous_features @@ -1563,9 +1563,9 @@ def allow_ambiguous_features(self) -> bool: @property def hook_config(self) -> dict[str, Any]: if self._hook_config is None: - hook_config: dict[str, Any] = self.config.get('hooks', {}) + hook_config: dict[str, Any] = self.config.get("hooks", {}) if not isinstance(hook_config, dict): - message = 'Field `tool.hatch.metadata.hooks` must be a table' + message = "Field `tool.hatch.metadata.hooks` must be a table" raise TypeError(message) self._hook_config = hook_config @@ -1583,7 +1583,7 @@ def hooks(self) -> dict[str, MetadataHookInterface]: if metadata_hook is None: from hatchling.plugin.exceptions import UnknownPluginError - message = f'Unknown metadata hook: {hook_name}' + message = f"Unknown metadata hook: {hook_name}" raise UnknownPluginError(message) configured_hooks[hook_name] = metadata_hook(self.root, config) @@ -1600,7 +1600,7 @@ def _resolve_optional_dependencies( return if dependent_option in visited: - message = f'Field `project.optional-dependencies` defines a circular dependency group: {dependent_option}' + message = f"Field `project.optional-dependencies` defines a circular dependency group: {dependent_option}" raise ValueError(message) visited.add(dependent_option) @@ -1611,7 +1611,7 @@ def _resolve_optional_dependencies( ) if selected_option not in optional_dependencies_complex: message = ( - f'Unknown recursive dependency group in field `project.optional-dependencies`: {selected_option}' + f"Unknown recursive dependency group in field `project.optional-dependencies`: {selected_option}" ) raise ValueError(message) diff --git a/backend/src/hatchling/metadata/custom.py b/backend/src/hatchling/metadata/custom.py index 2bf563f3b..649dcc695 100644 --- a/backend/src/hatchling/metadata/custom.py +++ b/backend/src/hatchling/metadata/custom.py @@ -9,7 +9,7 @@ class CustomMetadataHook: - PLUGIN_NAME = 'custom' + PLUGIN_NAME = "custom" def __new__( # type: ignore[misc] cls, @@ -18,21 +18,21 @@ def __new__( # type: ignore[misc] *args: Any, **kwargs: Any, ) -> MetadataHookInterface: - build_script = config.get('path', DEFAULT_BUILD_SCRIPT) + build_script = config.get("path", DEFAULT_BUILD_SCRIPT) if not isinstance(build_script, str): - message = f'Option `path` for metadata hook `{cls.PLUGIN_NAME}` must be a string' + message = f"Option `path` for metadata hook `{cls.PLUGIN_NAME}` must be a string" raise TypeError(message) if not build_script: - message = f'Option `path` for metadata hook `{cls.PLUGIN_NAME}` must not be empty if defined' + message = f"Option `path` for metadata hook `{cls.PLUGIN_NAME}` must not be empty if defined" raise ValueError(message) path = os.path.normpath(os.path.join(root, build_script)) if not os.path.isfile(path): - message = f'Build script does not exist: {build_script}' + message = f"Build script does not exist: {build_script}" raise OSError(message) - hook_class = load_plugin_from_script(path, build_script, MetadataHookInterface, 'metadata_hook') # type: ignore[type-abstract] + hook_class = load_plugin_from_script(path, build_script, MetadataHookInterface, "metadata_hook") # type: ignore[type-abstract] hook = hook_class(root, config, *args, **kwargs) # Always keep the name to avoid confusion diff --git a/backend/src/hatchling/metadata/plugin/interface.py b/backend/src/hatchling/metadata/plugin/interface.py index b6c8fdbf5..5cc1c4934 100644 --- a/backend/src/hatchling/metadata/plugin/interface.py +++ b/backend/src/hatchling/metadata/plugin/interface.py @@ -12,7 +12,7 @@ class MetadataHookInterface(ABC): # no cov class SpecialMetadataHook(MetadataHookInterface): - PLUGIN_NAME = 'special' + PLUGIN_NAME = "special" ... ``` @@ -28,7 +28,7 @@ def hatch_register_metadata_hook(): ``` """ - PLUGIN_NAME = '' + PLUGIN_NAME = "" """The name used for selection.""" def __init__(self, root: str, config: dict) -> None: diff --git a/backend/src/hatchling/metadata/spec.py b/backend/src/hatchling/metadata/spec.py index b295b7014..1b1d800bc 100644 --- a/backend/src/hatchling/metadata/spec.py +++ b/backend/src/hatchling/metadata/spec.py @@ -5,45 +5,45 @@ if TYPE_CHECKING: from hatchling.metadata.core import ProjectMetadata -DEFAULT_METADATA_VERSION = '2.4' -LATEST_METADATA_VERSION = '2.4' +DEFAULT_METADATA_VERSION = "2.4" +LATEST_METADATA_VERSION = "2.4" CORE_METADATA_PROJECT_FIELDS = { - 'Author': ('authors',), - 'Author-email': ('authors',), - 'Classifier': ('classifiers',), - 'Description': ('readme',), - 'Description-Content-Type': ('readme',), - 'Dynamic': ('dynamic',), - 'Keywords': ('keywords',), - 'License': ('license',), - 'License-Expression': ('license',), - 'License-Files': ('license-files',), - 'Maintainer': ('maintainers',), - 'Maintainer-email': ('maintainers',), - 'Name': ('name',), - 'Provides-Extra': ('dependencies', 'optional-dependencies'), - 'Requires-Dist': ('dependencies',), - 'Requires-Python': ('requires-python',), - 'Summary': ('description',), - 'Project-URL': ('urls',), - 'Version': ('version',), + "Author": ("authors",), + "Author-email": ("authors",), + "Classifier": ("classifiers",), + "Description": ("readme",), + "Description-Content-Type": ("readme",), + "Dynamic": ("dynamic",), + "Keywords": ("keywords",), + "License": ("license",), + "License-Expression": ("license",), + "License-Files": ("license-files",), + "Maintainer": ("maintainers",), + "Maintainer-email": ("maintainers",), + "Name": ("name",), + "Provides-Extra": ("dependencies", "optional-dependencies"), + "Requires-Dist": ("dependencies",), + "Requires-Python": ("requires-python",), + "Summary": ("description",), + "Project-URL": ("urls",), + "Version": ("version",), } PROJECT_CORE_METADATA_FIELDS = { - 'authors': ('Author', 'Author-email'), - 'classifiers': ('Classifier',), - 'dependencies': ('Requires-Dist',), - 'dynamic': ('Dynamic',), - 'keywords': ('Keywords',), - 'license': ('License', 'License-Expression'), - 'license-files': ('License-Files',), - 'maintainers': ('Maintainer', 'Maintainer-email'), - 'name': ('Name',), - 'optional-dependencies': ('Requires-Dist', 'Provides-Extra'), - 'readme': ('Description', 'Description-Content-Type'), - 'requires-python': ('Requires-Python',), - 'description': ('Summary',), - 'urls': ('Project-URL',), - 'version': ('Version',), + "authors": ("Author", "Author-email"), + "classifiers": ("Classifier",), + "dependencies": ("Requires-Dist",), + "dynamic": ("Dynamic",), + "keywords": ("Keywords",), + "license": ("License", "License-Expression"), + "license-files": ("License-Files",), + "maintainers": ("Maintainer", "Maintainer-email"), + "name": ("Name",), + "optional-dependencies": ("Requires-Dist", "Provides-Extra"), + "readme": ("Description", "Description-Content-Type"), + "requires-python": ("Requires-Python",), + "description": ("Summary",), + "urls": ("Project-URL",), + "version": ("Version",), } @@ -52,11 +52,11 @@ def get_core_metadata_constructors() -> dict[str, Callable]: https://packaging.python.org/specifications/core-metadata/ """ return { - '1.2': construct_metadata_file_1_2, - '2.1': construct_metadata_file_2_1, - '2.2': construct_metadata_file_2_2, - '2.3': construct_metadata_file_2_3, - '2.4': construct_metadata_file_2_4, + "1.2": construct_metadata_file_1_2, + "2.1": construct_metadata_file_2_1, + "2.2": construct_metadata_file_2_2, + "2.3": construct_metadata_file_2_3, + "2.4": construct_metadata_file_2_4, } @@ -70,96 +70,96 @@ def project_metadata_from_core_metadata(core_metadata: str) -> dict[str, Any]: message = email.message_from_string(core_metadata) metadata: dict[str, Any] = {} - if name := message.get('Name'): - metadata['name'] = name + if name := message.get("Name"): + metadata["name"] = name else: - error_message = 'Missing required core metadata: Name' + error_message = "Missing required core metadata: Name" raise ValueError(error_message) - if version := message.get('Version'): - metadata['version'] = version + if version := message.get("Version"): + metadata["version"] = version else: - error_message = 'Missing required core metadata: Version' + error_message = "Missing required core metadata: Version" raise ValueError(error_message) - if (dynamic_fields := message.get_all('Dynamic')) is not None: + if (dynamic_fields := message.get_all("Dynamic")) is not None: # Use as an ordered set to retain bidirectional formatting. # This likely doesn't matter but we try hard around here. - metadata['dynamic'] = list({ + metadata["dynamic"] = list({ project_field: None for core_metadata_field in dynamic_fields for project_field in CORE_METADATA_PROJECT_FIELDS.get(core_metadata_field, ()) }) if description := message.get_payload(): - metadata['readme'] = { - 'content-type': message.get('Description-Content-Type', 'text/plain'), - 'text': description, + metadata["readme"] = { + "content-type": message.get("Description-Content-Type", "text/plain"), + "text": description, } - if (license_expression := message.get('License-Expression')) is not None: - metadata['license'] = license_expression - elif (license_text := message.get('License')) is not None: - metadata['license'] = {'text': license_text} + if (license_expression := message.get("License-Expression")) is not None: + metadata["license"] = license_expression + elif (license_text := message.get("License")) is not None: + metadata["license"] = {"text": license_text} - if (license_files := message.get_all('License-File')) is not None: - metadata['license-files'] = license_files + if (license_files := message.get_all("License-File")) is not None: + metadata["license-files"] = license_files - if (summary := message.get('Summary')) is not None: - metadata['description'] = summary + if (summary := message.get("Summary")) is not None: + metadata["description"] = summary - if (keywords := message.get('Keywords')) is not None: - metadata['keywords'] = keywords.split(',') + if (keywords := message.get("Keywords")) is not None: + metadata["keywords"] = keywords.split(",") - if (classifiers := message.get_all('Classifier')) is not None: - metadata['classifiers'] = classifiers + if (classifiers := message.get_all("Classifier")) is not None: + metadata["classifiers"] = classifiers - if (project_urls := message.get_all('Project-URL')) is not None: + if (project_urls := message.get_all("Project-URL")) is not None: urls = {} for project_url in project_urls: - label, url = project_url.split(',', maxsplit=1) + label, url = project_url.split(",", maxsplit=1) urls[label.strip()] = url.strip() - metadata['urls'] = urls + metadata["urls"] = urls authors = [] - if (author := message.get('Author')) is not None: - authors.append({'name': author}) + if (author := message.get("Author")) is not None: + authors.append({"name": author}) - if (author_email := message.get('Author-email')) is not None: - address_header = header_registry('resent-from', author_email) + if (author_email := message.get("Author-email")) is not None: + address_header = header_registry("resent-from", author_email) for address in address_header.addresses: # type: ignore[attr-defined] - data = {'email': address.addr_spec} + data = {"email": address.addr_spec} if name := address.display_name: - data['name'] = name + data["name"] = name authors.append(data) if authors: - metadata['authors'] = authors + metadata["authors"] = authors maintainers = [] - if (maintainer := message.get('Maintainer')) is not None: - maintainers.append({'name': maintainer}) + if (maintainer := message.get("Maintainer")) is not None: + maintainers.append({"name": maintainer}) - if (maintainer_email := message.get('Maintainer-email')) is not None: - address_header = header_registry('resent-from', maintainer_email) + if (maintainer_email := message.get("Maintainer-email")) is not None: + address_header = header_registry("resent-from", maintainer_email) for address in address_header.addresses: # type: ignore[attr-defined] - data = {'email': address.addr_spec} + data = {"email": address.addr_spec} if name := address.display_name: - data['name'] = name + data["name"] = name maintainers.append(data) if maintainers: - metadata['maintainers'] = maintainers + metadata["maintainers"] = maintainers - if (requires_python := message.get('Requires-Python')) is not None: - metadata['requires-python'] = requires_python + if (requires_python := message.get("Requires-Python")) is not None: + metadata["requires-python"] = requires_python optional_dependencies: dict[str, list[str]] = {} - if (extras := message.get_all('Provides-Extra')) is not None: + if (extras := message.get_all("Provides-Extra")) is not None: for extra in extras: optional_dependencies[extra] = [] - if (requirements := message.get_all('Requires-Dist')) is not None: + if (requirements := message.get_all("Requires-Dist")) is not None: from packaging.requirements import Requirement dependencies = [] @@ -173,7 +173,7 @@ def project_metadata_from_core_metadata(core_metadata: str) -> dict[str, Any]: for i, marker in enumerate(markers): if isinstance(marker, tuple): left, _, right = marker - if left.value == 'extra': + if left.value == "extra": extra = right.value del markers[i] # noqa: B909 # If there was only one marker then there will be an unnecessary @@ -189,10 +189,10 @@ def project_metadata_from_core_metadata(core_metadata: str) -> dict[str, Any]: else: dependencies.append(str(req)) - metadata['dependencies'] = dependencies + metadata["dependencies"] = dependencies if optional_dependencies: - metadata['optional-dependencies'] = optional_dependencies + metadata["optional-dependencies"] = optional_dependencies return metadata @@ -201,59 +201,59 @@ def construct_metadata_file_1_2(metadata: ProjectMetadata, extra_dependencies: t """ https://peps.python.org/pep-0345/ """ - metadata_file = 'Metadata-Version: 1.2\n' - metadata_file += f'Name: {metadata.core.raw_name}\n' - metadata_file += f'Version: {metadata.version}\n' + metadata_file = "Metadata-Version: 1.2\n" + metadata_file += f"Name: {metadata.core.raw_name}\n" + metadata_file += f"Version: {metadata.version}\n" if metadata.core.description: - metadata_file += f'Summary: {metadata.core.description}\n' + metadata_file += f"Summary: {metadata.core.description}\n" if metadata.core.urls: for label, url in metadata.core.urls.items(): - metadata_file += f'Project-URL: {label}, {url}\n' + metadata_file += f"Project-URL: {label}, {url}\n" authors_data = metadata.core.authors_data - if authors_data['name']: + if authors_data["name"]: metadata_file += f"Author: {', '.join(authors_data['name'])}\n" - if authors_data['email']: + if authors_data["email"]: metadata_file += f"Author-email: {', '.join(authors_data['email'])}\n" maintainers_data = metadata.core.maintainers_data - if maintainers_data['name']: + if maintainers_data["name"]: metadata_file += f"Maintainer: {', '.join(maintainers_data['name'])}\n" - if maintainers_data['email']: + if maintainers_data["email"]: metadata_file += f"Maintainer-email: {', '.join(maintainers_data['email'])}\n" if metadata.core.license: - license_start = 'License: ' - indent = ' ' * (len(license_start) - 1) + license_start = "License: " + indent = " " * (len(license_start) - 1) metadata_file += license_start for i, line in enumerate(metadata.core.license.splitlines()): if i == 0: - metadata_file += f'{line}\n' + metadata_file += f"{line}\n" else: - metadata_file += f'{indent}{line}\n' + metadata_file += f"{indent}{line}\n" elif metadata.core.license_expression: - metadata_file += f'License: {metadata.core.license_expression}\n' + metadata_file += f"License: {metadata.core.license_expression}\n" if metadata.core.keywords: metadata_file += f"Keywords: {','.join(metadata.core.keywords)}\n" if metadata.core.classifiers: for classifier in metadata.core.classifiers: - metadata_file += f'Classifier: {classifier}\n' + metadata_file += f"Classifier: {classifier}\n" if metadata.core.requires_python: - metadata_file += f'Requires-Python: {metadata.core.requires_python}\n' + metadata_file += f"Requires-Python: {metadata.core.requires_python}\n" if metadata.core.dependencies: for dependency in metadata.core.dependencies: - metadata_file += f'Requires-Dist: {dependency}\n' + metadata_file += f"Requires-Dist: {dependency}\n" if extra_dependencies: for dependency in extra_dependencies: - metadata_file += f'Requires-Dist: {dependency}\n' + metadata_file += f"Requires-Dist: {dependency}\n" return metadata_file @@ -262,75 +262,75 @@ def construct_metadata_file_2_1(metadata: ProjectMetadata, extra_dependencies: t """ https://peps.python.org/pep-0566/ """ - metadata_file = 'Metadata-Version: 2.1\n' - metadata_file += f'Name: {metadata.core.raw_name}\n' - metadata_file += f'Version: {metadata.version}\n' + metadata_file = "Metadata-Version: 2.1\n" + metadata_file += f"Name: {metadata.core.raw_name}\n" + metadata_file += f"Version: {metadata.version}\n" if metadata.core.description: - metadata_file += f'Summary: {metadata.core.description}\n' + metadata_file += f"Summary: {metadata.core.description}\n" if metadata.core.urls: for label, url in metadata.core.urls.items(): - metadata_file += f'Project-URL: {label}, {url}\n' + metadata_file += f"Project-URL: {label}, {url}\n" authors_data = metadata.core.authors_data - if authors_data['name']: + if authors_data["name"]: metadata_file += f"Author: {', '.join(authors_data['name'])}\n" - if authors_data['email']: + if authors_data["email"]: metadata_file += f"Author-email: {', '.join(authors_data['email'])}\n" maintainers_data = metadata.core.maintainers_data - if maintainers_data['name']: + if maintainers_data["name"]: metadata_file += f"Maintainer: {', '.join(maintainers_data['name'])}\n" - if maintainers_data['email']: + if maintainers_data["email"]: metadata_file += f"Maintainer-email: {', '.join(maintainers_data['email'])}\n" if metadata.core.license: - license_start = 'License: ' - indent = ' ' * (len(license_start) - 1) + license_start = "License: " + indent = " " * (len(license_start) - 1) metadata_file += license_start for i, line in enumerate(metadata.core.license.splitlines()): if i == 0: - metadata_file += f'{line}\n' + metadata_file += f"{line}\n" else: - metadata_file += f'{indent}{line}\n' + metadata_file += f"{indent}{line}\n" elif metadata.core.license_expression: - metadata_file += f'License: {metadata.core.license_expression}\n' + metadata_file += f"License: {metadata.core.license_expression}\n" if metadata.core.keywords: metadata_file += f"Keywords: {','.join(metadata.core.keywords)}\n" if metadata.core.classifiers: for classifier in metadata.core.classifiers: - metadata_file += f'Classifier: {classifier}\n' + metadata_file += f"Classifier: {classifier}\n" if metadata.core.requires_python: - metadata_file += f'Requires-Python: {metadata.core.requires_python}\n' + metadata_file += f"Requires-Python: {metadata.core.requires_python}\n" if metadata.core.dependencies: for dependency in metadata.core.dependencies: - metadata_file += f'Requires-Dist: {dependency}\n' + metadata_file += f"Requires-Dist: {dependency}\n" if extra_dependencies: for dependency in extra_dependencies: - metadata_file += f'Requires-Dist: {dependency}\n' + metadata_file += f"Requires-Dist: {dependency}\n" if metadata.core.optional_dependencies: for option, dependencies in metadata.core.optional_dependencies.items(): - metadata_file += f'Provides-Extra: {option}\n' + metadata_file += f"Provides-Extra: {option}\n" for dependency in dependencies: - if ';' in dependency: - dep_name, dep_env_marker = dependency.split(';', maxsplit=1) - metadata_file += f'Requires-Dist: {dep_name}; ({dep_env_marker.strip()}) and extra == {option!r}\n' - elif '@ ' in dependency: - metadata_file += f'Requires-Dist: {dependency} ; extra == {option!r}\n' + if ";" in dependency: + dep_name, dep_env_marker = dependency.split(";", maxsplit=1) + metadata_file += f"Requires-Dist: {dep_name}; ({dep_env_marker.strip()}) and extra == {option!r}\n" + elif "@ " in dependency: + metadata_file += f"Requires-Dist: {dependency} ; extra == {option!r}\n" else: - metadata_file += f'Requires-Dist: {dependency}; extra == {option!r}\n' + metadata_file += f"Requires-Dist: {dependency}; extra == {option!r}\n" if metadata.core.readme: - metadata_file += f'Description-Content-Type: {metadata.core.readme_content_type}\n' - metadata_file += f'\n{metadata.core.readme}' + metadata_file += f"Description-Content-Type: {metadata.core.readme_content_type}\n" + metadata_file += f"\n{metadata.core.readme}" return metadata_file @@ -339,9 +339,9 @@ def construct_metadata_file_2_2(metadata: ProjectMetadata, extra_dependencies: t """ https://peps.python.org/pep-0643/ """ - metadata_file = 'Metadata-Version: 2.2\n' - metadata_file += f'Name: {metadata.core.raw_name}\n' - metadata_file += f'Version: {metadata.version}\n' + metadata_file = "Metadata-Version: 2.2\n" + metadata_file += f"Name: {metadata.core.raw_name}\n" + metadata_file += f"Version: {metadata.version}\n" if metadata.core.dynamic: # Ordered set @@ -350,73 +350,73 @@ def construct_metadata_file_2_2(metadata: ProjectMetadata, extra_dependencies: t for project_field in metadata.core.dynamic for core_metadata_field in PROJECT_CORE_METADATA_FIELDS.get(project_field, ()) }: - metadata_file += f'Dynamic: {field}\n' + metadata_file += f"Dynamic: {field}\n" if metadata.core.description: - metadata_file += f'Summary: {metadata.core.description}\n' + metadata_file += f"Summary: {metadata.core.description}\n" if metadata.core.urls: for label, url in metadata.core.urls.items(): - metadata_file += f'Project-URL: {label}, {url}\n' + metadata_file += f"Project-URL: {label}, {url}\n" authors_data = metadata.core.authors_data - if authors_data['name']: + if authors_data["name"]: metadata_file += f"Author: {', '.join(authors_data['name'])}\n" - if authors_data['email']: + if authors_data["email"]: metadata_file += f"Author-email: {', '.join(authors_data['email'])}\n" maintainers_data = metadata.core.maintainers_data - if maintainers_data['name']: + if maintainers_data["name"]: metadata_file += f"Maintainer: {', '.join(maintainers_data['name'])}\n" - if maintainers_data['email']: + if maintainers_data["email"]: metadata_file += f"Maintainer-email: {', '.join(maintainers_data['email'])}\n" if metadata.core.license: - license_start = 'License: ' - indent = ' ' * (len(license_start) - 1) + license_start = "License: " + indent = " " * (len(license_start) - 1) metadata_file += license_start for i, line in enumerate(metadata.core.license.splitlines()): if i == 0: - metadata_file += f'{line}\n' + metadata_file += f"{line}\n" else: - metadata_file += f'{indent}{line}\n' + metadata_file += f"{indent}{line}\n" elif metadata.core.license_expression: - metadata_file += f'License: {metadata.core.license_expression}\n' + metadata_file += f"License: {metadata.core.license_expression}\n" if metadata.core.keywords: metadata_file += f"Keywords: {','.join(metadata.core.keywords)}\n" if metadata.core.classifiers: for classifier in metadata.core.classifiers: - metadata_file += f'Classifier: {classifier}\n' + metadata_file += f"Classifier: {classifier}\n" if metadata.core.requires_python: - metadata_file += f'Requires-Python: {metadata.core.requires_python}\n' + metadata_file += f"Requires-Python: {metadata.core.requires_python}\n" if metadata.core.dependencies: for dependency in metadata.core.dependencies: - metadata_file += f'Requires-Dist: {dependency}\n' + metadata_file += f"Requires-Dist: {dependency}\n" if extra_dependencies: for dependency in extra_dependencies: - metadata_file += f'Requires-Dist: {dependency}\n' + metadata_file += f"Requires-Dist: {dependency}\n" if metadata.core.optional_dependencies: for option, dependencies in metadata.core.optional_dependencies.items(): - metadata_file += f'Provides-Extra: {option}\n' + metadata_file += f"Provides-Extra: {option}\n" for dependency in dependencies: - if ';' in dependency: - dep_name, dep_env_marker = dependency.split(';', maxsplit=1) - metadata_file += f'Requires-Dist: {dep_name}; ({dep_env_marker.strip()}) and extra == {option!r}\n' - elif '@ ' in dependency: - metadata_file += f'Requires-Dist: {dependency} ; extra == {option!r}\n' + if ";" in dependency: + dep_name, dep_env_marker = dependency.split(";", maxsplit=1) + metadata_file += f"Requires-Dist: {dep_name}; ({dep_env_marker.strip()}) and extra == {option!r}\n" + elif "@ " in dependency: + metadata_file += f"Requires-Dist: {dependency} ; extra == {option!r}\n" else: - metadata_file += f'Requires-Dist: {dependency}; extra == {option!r}\n' + metadata_file += f"Requires-Dist: {dependency}; extra == {option!r}\n" if metadata.core.readme: - metadata_file += f'Description-Content-Type: {metadata.core.readme_content_type}\n' - metadata_file += f'\n{metadata.core.readme}' + metadata_file += f"Description-Content-Type: {metadata.core.readme_content_type}\n" + metadata_file += f"\n{metadata.core.readme}" return metadata_file @@ -425,9 +425,9 @@ def construct_metadata_file_2_3(metadata: ProjectMetadata, extra_dependencies: t """ https://peps.python.org/pep-0685/ """ - metadata_file = 'Metadata-Version: 2.3\n' - metadata_file += f'Name: {metadata.core.raw_name}\n' - metadata_file += f'Version: {metadata.version}\n' + metadata_file = "Metadata-Version: 2.3\n" + metadata_file += f"Name: {metadata.core.raw_name}\n" + metadata_file += f"Version: {metadata.version}\n" if metadata.core.dynamic: # Ordered set @@ -436,73 +436,73 @@ def construct_metadata_file_2_3(metadata: ProjectMetadata, extra_dependencies: t for project_field in metadata.core.dynamic for core_metadata_field in PROJECT_CORE_METADATA_FIELDS.get(project_field, ()) }: - metadata_file += f'Dynamic: {field}\n' + metadata_file += f"Dynamic: {field}\n" if metadata.core.description: - metadata_file += f'Summary: {metadata.core.description}\n' + metadata_file += f"Summary: {metadata.core.description}\n" if metadata.core.urls: for label, url in metadata.core.urls.items(): - metadata_file += f'Project-URL: {label}, {url}\n' + metadata_file += f"Project-URL: {label}, {url}\n" authors_data = metadata.core.authors_data - if authors_data['name']: + if authors_data["name"]: metadata_file += f"Author: {', '.join(authors_data['name'])}\n" - if authors_data['email']: + if authors_data["email"]: metadata_file += f"Author-email: {', '.join(authors_data['email'])}\n" maintainers_data = metadata.core.maintainers_data - if maintainers_data['name']: + if maintainers_data["name"]: metadata_file += f"Maintainer: {', '.join(maintainers_data['name'])}\n" - if maintainers_data['email']: + if maintainers_data["email"]: metadata_file += f"Maintainer-email: {', '.join(maintainers_data['email'])}\n" if metadata.core.license: - license_start = 'License: ' - indent = ' ' * (len(license_start) - 1) + license_start = "License: " + indent = " " * (len(license_start) - 1) metadata_file += license_start for i, line in enumerate(metadata.core.license.splitlines()): if i == 0: - metadata_file += f'{line}\n' + metadata_file += f"{line}\n" else: - metadata_file += f'{indent}{line}\n' + metadata_file += f"{indent}{line}\n" elif metadata.core.license_expression: - metadata_file += f'License: {metadata.core.license_expression}\n' + metadata_file += f"License: {metadata.core.license_expression}\n" if metadata.core.keywords: metadata_file += f"Keywords: {','.join(metadata.core.keywords)}\n" if metadata.core.classifiers: for classifier in metadata.core.classifiers: - metadata_file += f'Classifier: {classifier}\n' + metadata_file += f"Classifier: {classifier}\n" if metadata.core.requires_python: - metadata_file += f'Requires-Python: {metadata.core.requires_python}\n' + metadata_file += f"Requires-Python: {metadata.core.requires_python}\n" if metadata.core.dependencies: for dependency in metadata.core.dependencies: - metadata_file += f'Requires-Dist: {dependency}\n' + metadata_file += f"Requires-Dist: {dependency}\n" if extra_dependencies: for dependency in extra_dependencies: - metadata_file += f'Requires-Dist: {dependency}\n' + metadata_file += f"Requires-Dist: {dependency}\n" if metadata.core.optional_dependencies: for option, dependencies in metadata.core.optional_dependencies.items(): - metadata_file += f'Provides-Extra: {option}\n' + metadata_file += f"Provides-Extra: {option}\n" for dependency in dependencies: - if ';' in dependency: - dep_name, dep_env_marker = dependency.split(';', maxsplit=1) - metadata_file += f'Requires-Dist: {dep_name}; ({dep_env_marker.strip()}) and extra == {option!r}\n' - elif '@ ' in dependency: - metadata_file += f'Requires-Dist: {dependency} ; extra == {option!r}\n' + if ";" in dependency: + dep_name, dep_env_marker = dependency.split(";", maxsplit=1) + metadata_file += f"Requires-Dist: {dep_name}; ({dep_env_marker.strip()}) and extra == {option!r}\n" + elif "@ " in dependency: + metadata_file += f"Requires-Dist: {dependency} ; extra == {option!r}\n" else: - metadata_file += f'Requires-Dist: {dependency}; extra == {option!r}\n' + metadata_file += f"Requires-Dist: {dependency}; extra == {option!r}\n" if metadata.core.readme: - metadata_file += f'Description-Content-Type: {metadata.core.readme_content_type}\n' - metadata_file += f'\n{metadata.core.readme}' + metadata_file += f"Description-Content-Type: {metadata.core.readme_content_type}\n" + metadata_file += f"\n{metadata.core.readme}" return metadata_file @@ -511,9 +511,9 @@ def construct_metadata_file_2_4(metadata: ProjectMetadata, extra_dependencies: t """ https://peps.python.org/pep-0639/ """ - metadata_file = 'Metadata-Version: 2.4\n' - metadata_file += f'Name: {metadata.core.raw_name}\n' - metadata_file += f'Version: {metadata.version}\n' + metadata_file = "Metadata-Version: 2.4\n" + metadata_file += f"Name: {metadata.core.raw_name}\n" + metadata_file += f"Version: {metadata.version}\n" if metadata.core.dynamic: # Ordered set @@ -522,77 +522,77 @@ def construct_metadata_file_2_4(metadata: ProjectMetadata, extra_dependencies: t for project_field in metadata.core.dynamic for core_metadata_field in PROJECT_CORE_METADATA_FIELDS.get(project_field, ()) }: - metadata_file += f'Dynamic: {field}\n' + metadata_file += f"Dynamic: {field}\n" if metadata.core.description: - metadata_file += f'Summary: {metadata.core.description}\n' + metadata_file += f"Summary: {metadata.core.description}\n" if metadata.core.urls: for label, url in metadata.core.urls.items(): - metadata_file += f'Project-URL: {label}, {url}\n' + metadata_file += f"Project-URL: {label}, {url}\n" authors_data = metadata.core.authors_data - if authors_data['name']: + if authors_data["name"]: metadata_file += f"Author: {', '.join(authors_data['name'])}\n" - if authors_data['email']: + if authors_data["email"]: metadata_file += f"Author-email: {', '.join(authors_data['email'])}\n" maintainers_data = metadata.core.maintainers_data - if maintainers_data['name']: + if maintainers_data["name"]: metadata_file += f"Maintainer: {', '.join(maintainers_data['name'])}\n" - if maintainers_data['email']: + if maintainers_data["email"]: metadata_file += f"Maintainer-email: {', '.join(maintainers_data['email'])}\n" if metadata.core.license: - license_start = 'License: ' - indent = ' ' * (len(license_start) - 1) + license_start = "License: " + indent = " " * (len(license_start) - 1) metadata_file += license_start for i, line in enumerate(metadata.core.license.splitlines()): if i == 0: - metadata_file += f'{line}\n' + metadata_file += f"{line}\n" else: - metadata_file += f'{indent}{line}\n' + metadata_file += f"{indent}{line}\n" if metadata.core.license_expression: - metadata_file += f'License-Expression: {metadata.core.license_expression}\n' + metadata_file += f"License-Expression: {metadata.core.license_expression}\n" if metadata.core.license_files: for license_file in metadata.core.license_files: - metadata_file += f'License-File: {license_file}\n' + metadata_file += f"License-File: {license_file}\n" if metadata.core.keywords: metadata_file += f"Keywords: {','.join(metadata.core.keywords)}\n" if metadata.core.classifiers: for classifier in metadata.core.classifiers: - metadata_file += f'Classifier: {classifier}\n' + metadata_file += f"Classifier: {classifier}\n" if metadata.core.requires_python: - metadata_file += f'Requires-Python: {metadata.core.requires_python}\n' + metadata_file += f"Requires-Python: {metadata.core.requires_python}\n" if metadata.core.dependencies: for dependency in metadata.core.dependencies: - metadata_file += f'Requires-Dist: {dependency}\n' + metadata_file += f"Requires-Dist: {dependency}\n" if extra_dependencies: for dependency in extra_dependencies: - metadata_file += f'Requires-Dist: {dependency}\n' + metadata_file += f"Requires-Dist: {dependency}\n" if metadata.core.optional_dependencies: for option, dependencies in metadata.core.optional_dependencies.items(): - metadata_file += f'Provides-Extra: {option}\n' + metadata_file += f"Provides-Extra: {option}\n" for dependency in dependencies: - if ';' in dependency: - dep_name, dep_env_marker = dependency.split(';', maxsplit=1) - metadata_file += f'Requires-Dist: {dep_name}; ({dep_env_marker.strip()}) and extra == {option!r}\n' - elif '@ ' in dependency: - metadata_file += f'Requires-Dist: {dependency} ; extra == {option!r}\n' + if ";" in dependency: + dep_name, dep_env_marker = dependency.split(";", maxsplit=1) + metadata_file += f"Requires-Dist: {dep_name}; ({dep_env_marker.strip()}) and extra == {option!r}\n" + elif "@ " in dependency: + metadata_file += f"Requires-Dist: {dependency} ; extra == {option!r}\n" else: - metadata_file += f'Requires-Dist: {dependency}; extra == {option!r}\n' + metadata_file += f"Requires-Dist: {dependency}; extra == {option!r}\n" if metadata.core.readme: - metadata_file += f'Description-Content-Type: {metadata.core.readme_content_type}\n' - metadata_file += f'\n{metadata.core.readme}' + metadata_file += f"Description-Content-Type: {metadata.core.readme_content_type}\n" + metadata_file += f"\n{metadata.core.readme}" return metadata_file diff --git a/backend/src/hatchling/metadata/utils.py b/backend/src/hatchling/metadata/utils.py index e7cd9005d..270d1730e 100644 --- a/backend/src/hatchling/metadata/utils.py +++ b/backend/src/hatchling/metadata/utils.py @@ -13,12 +13,12 @@ def is_valid_project_name(project_name: str) -> bool: # https://peps.python.org/pep-0508/#names - return re.search('^([A-Z0-9]|[A-Z0-9][A-Z0-9._-]*[A-Z0-9])$', project_name, re.IGNORECASE) is not None + return re.search("^([A-Z0-9]|[A-Z0-9][A-Z0-9._-]*[A-Z0-9])$", project_name, re.IGNORECASE) is not None def normalize_project_name(project_name: str) -> str: # https://peps.python.org/pep-0503/#normalized-names - return re.sub(r'[-_.]+', '-', project_name).lower() + return re.sub(r"[-_.]+", "-", project_name).lower() def normalize_requirement(requirement: Requirement) -> None: @@ -47,20 +47,20 @@ def get_normalized_dependency(requirement: Requirement) -> str: def resolve_metadata_fields(metadata: ProjectMetadata) -> dict[str, Any]: # https://packaging.python.org/en/latest/specifications/declaring-project-metadata/ return { - 'name': metadata.core.name, - 'version': metadata.version, - 'description': metadata.core.description, - 'readme': {'content-type': metadata.core.readme_content_type, 'text': metadata.core.readme}, - 'requires-python': metadata.core.requires_python, - 'license': metadata.core.license_expression or metadata.core.license, - 'authors': metadata.core.authors, - 'maintainers': metadata.core.maintainers, - 'keywords': metadata.core.keywords, - 'classifiers': metadata.core.classifiers, - 'urls': metadata.core.urls, - 'scripts': metadata.core.scripts, - 'gui-scripts': metadata.core.gui_scripts, - 'entry-points': metadata.core.entry_points, - 'dependencies': metadata.core.dependencies, - 'optional-dependencies': metadata.core.optional_dependencies, + "name": metadata.core.name, + "version": metadata.version, + "description": metadata.core.description, + "readme": {"content-type": metadata.core.readme_content_type, "text": metadata.core.readme}, + "requires-python": metadata.core.requires_python, + "license": metadata.core.license_expression or metadata.core.license, + "authors": metadata.core.authors, + "maintainers": metadata.core.maintainers, + "keywords": metadata.core.keywords, + "classifiers": metadata.core.classifiers, + "urls": metadata.core.urls, + "scripts": metadata.core.scripts, + "gui-scripts": metadata.core.gui_scripts, + "entry-points": metadata.core.entry_points, + "dependencies": metadata.core.dependencies, + "optional-dependencies": metadata.core.optional_dependencies, } diff --git a/backend/src/hatchling/ouroboros.py b/backend/src/hatchling/ouroboros.py index 7e6e8b816..4c7b440e0 100644 --- a/backend/src/hatchling/ouroboros.py +++ b/backend/src/hatchling/ouroboros.py @@ -9,15 +9,15 @@ def read_dependencies() -> list[str]: - pattern = r'^dependencies = (\[.*?\])$' + pattern = r"^dependencies = (\[.*?\])$" - with open(os.path.join(os.getcwd(), 'pyproject.toml'), encoding='utf-8') as f: + with open(os.path.join(os.getcwd(), "pyproject.toml"), encoding="utf-8") as f: # Windows \r\n prevents match - contents = '\n'.join(line.rstrip() for line in f) + contents = "\n".join(line.rstrip() for line in f) match = re.search(pattern, contents, flags=re.MULTILINE | re.DOTALL) if match is None: - message = 'No dependencies found' + message = "No dependencies found" raise ValueError(message) return literal_eval(match.group(1)) diff --git a/backend/src/hatchling/plugin/__init__.py b/backend/src/hatchling/plugin/__init__.py index 07fdce47c..a42736d87 100644 --- a/backend/src/hatchling/plugin/__init__.py +++ b/backend/src/hatchling/plugin/__init__.py @@ -1,3 +1,3 @@ import pluggy -hookimpl = pluggy.HookimplMarker('hatch') +hookimpl = pluggy.HookimplMarker("hatch") diff --git a/backend/src/hatchling/plugin/manager.py b/backend/src/hatchling/plugin/manager.py index 70773cccc..0a278afcd 100644 --- a/backend/src/hatchling/plugin/manager.py +++ b/backend/src/hatchling/plugin/manager.py @@ -7,7 +7,7 @@ class PluginManager: def __init__(self) -> None: - self.manager = pluggy.PluginManager('hatch') + self.manager = pluggy.PluginManager("hatch") self.third_party_plugins = ThirdPartyPlugins(self.manager) self.initialized = False @@ -21,12 +21,12 @@ def __getattr__(self, name: str) -> ClassRegister: self.initialize() self.initialized = True - hook_name = f'hatch_register_{name}' + hook_name = f"hatch_register_{name}" hook = getattr(self, hook_name, None) if hook: hook() - register = ClassRegister(getattr(self.manager.hook, hook_name), 'PLUGIN_NAME', self.third_party_plugins) + register = ClassRegister(getattr(self.manager.hook, hook_name), "PLUGIN_NAME", self.third_party_plugins) setattr(self, name, register) return register @@ -75,13 +75,13 @@ def collect(self, *, include_third_party: bool = True) -> dict: for registered_class in registered_classes: name = getattr(registered_class, self.identifier, None) if not name: # no cov - message = f'Class `{registered_class.__name__}` does not have a {name} attribute.' + message = f"Class `{registered_class.__name__}` does not have a {name} attribute." raise ValueError(message) if name in classes: # no cov message = ( - f'Class `{registered_class.__name__}` defines its name as `{name}` but ' - f'that name is already used by `{classes[name].__name__}`.' + f"Class `{registered_class.__name__}` defines its name as `{name}` but " + f"that name is already used by `{classes[name].__name__}`." ) raise ValueError(message) @@ -104,8 +104,8 @@ def __init__(self, manager: pluggy.PluginManager) -> None: self.loaded = False def load(self) -> None: - self.manager.load_setuptools_entrypoints('hatch') + self.manager.load_setuptools_entrypoints("hatch") self.loaded = True -PluginManagerBound = TypeVar('PluginManagerBound', bound=PluginManager) +PluginManagerBound = TypeVar("PluginManagerBound", bound=PluginManager) diff --git a/backend/src/hatchling/plugin/specs.py b/backend/src/hatchling/plugin/specs.py index 0fd12d23e..53e75efcc 100644 --- a/backend/src/hatchling/plugin/specs.py +++ b/backend/src/hatchling/plugin/specs.py @@ -1,6 +1,6 @@ import pluggy -hookspec = pluggy.HookspecMarker('hatch') +hookspec = pluggy.HookspecMarker("hatch") @hookspec diff --git a/backend/src/hatchling/plugin/utils.py b/backend/src/hatchling/plugin/utils.py index 841205422..41e52a66f 100644 --- a/backend/src/hatchling/plugin/utils.py +++ b/backend/src/hatchling/plugin/utils.py @@ -7,7 +7,7 @@ from hatchling.builders.plugin.interface import BuilderInterface from hatchling.metadata.plugin.interface import MetadataHookInterface - T = TypeVar('T', BuilderInterface, BuildHookInterface, MetadataHookInterface) + T = TypeVar("T", BuilderInterface, BuildHookInterface, MetadataHookInterface) def load_plugin_from_script(path: str, script_name: str, plugin_class: type[T], plugin_id: str) -> type[T]: @@ -17,7 +17,7 @@ def load_plugin_from_script(path: str, script_name: str, plugin_class: type[T], module = module_from_spec(spec) # type: ignore[arg-type] spec.loader.exec_module(module) # type: ignore[union-attr] - plugin_finder = f'get_{plugin_id}' + plugin_finder = f"get_{plugin_id}" names = dir(module) if plugin_finder in names: return getattr(module, plugin_finder)() @@ -35,13 +35,13 @@ def load_plugin_from_script(path: str, script_name: str, plugin_class: type[T], continue if not subclasses: - message = f'Unable to find a subclass of `{plugin_class.__name__}` in `{script_name}`: {path}' + message = f"Unable to find a subclass of `{plugin_class.__name__}` in `{script_name}`: {path}" raise ValueError(message) if len(subclasses) > 1: message = ( - f'Multiple subclasses of `{plugin_class.__name__}` found in `{script_name}`, ' - f'select one by defining a function named `{plugin_finder}`: {path}' + f"Multiple subclasses of `{plugin_class.__name__}` found in `{script_name}`, " + f"select one by defining a function named `{plugin_finder}`: {path}" ) raise ValueError(message) diff --git a/backend/src/hatchling/utils/constants.py b/backend/src/hatchling/utils/constants.py index e3349c7b5..52e33bca6 100644 --- a/backend/src/hatchling/utils/constants.py +++ b/backend/src/hatchling/utils/constants.py @@ -1,6 +1,6 @@ -DEFAULT_BUILD_SCRIPT = 'hatch_build.py' -DEFAULT_CONFIG_FILE = 'hatch.toml' +DEFAULT_BUILD_SCRIPT = "hatch_build.py" +DEFAULT_CONFIG_FILE = "hatch.toml" class VersionEnvVars: - VALIDATE_BUMP = 'HATCH_VERSION_VALIDATE_BUMP' + VALIDATE_BUMP = "HATCH_VERSION_VALIDATE_BUMP" diff --git a/backend/src/hatchling/utils/context.py b/backend/src/hatchling/utils/context.py index 8b3005957..bd18138b8 100644 --- a/backend/src/hatchling/utils/context.py +++ b/backend/src/hatchling/utils/context.py @@ -29,8 +29,8 @@ def format_path(cls, path: str, modifier: str) -> str: if not modifier: return os.path.normpath(path) - modifiers = modifier.split(':')[::-1] - while modifiers and modifiers[-1] == 'parent': + modifiers = modifier.split(":")[::-1] + while modifiers and modifiers[-1] == "parent": path = os.path.dirname(path) modifiers.pop() @@ -38,33 +38,33 @@ def format_path(cls, path: str, modifier: str) -> str: return path if len(modifiers) > 1: - message = f'Expected a single path modifier and instead got: {", ".join(reversed(modifiers))}' + message = f"Expected a single path modifier and instead got: {', '.join(reversed(modifiers))}" raise ValueError(message) modifier = modifiers[0] - if modifier == 'uri': + if modifier == "uri": return path_to_uri(path) - if modifier == 'real': + if modifier == "real": return os.path.realpath(path) - message = f'Unknown path modifier: {modifier}' + message = f"Unknown path modifier: {modifier}" raise ValueError(message) class DefaultContextFormatter(ContextFormatter): - CONTEXT_NAME = 'default' + CONTEXT_NAME = "default" def __init__(self, root: str) -> None: self.__root = root def get_formatters(self) -> MutableMapping: return { - '/': self.__format_directory_separator, - ';': self.__format_path_separator, - 'env': self.__format_env, - 'home': self.__format_home, - 'root': self.__format_root, + "/": self.__format_directory_separator, + ";": self.__format_path_separator, + "env": self.__format_env, + "home": self.__format_home, + "root": self.__format_root, } def __format_directory_separator(self, value: str, data: str) -> str: # noqa: ARG002, PLR6301 @@ -77,19 +77,19 @@ def __format_root(self, value: str, data: str) -> str: # noqa: ARG002 return self.format_path(self.__root, data) def __format_home(self, value: str, data: str) -> str: # noqa: ARG002 - return self.format_path(os.path.expanduser('~'), data) + return self.format_path(os.path.expanduser("~"), data) def __format_env(self, value: str, data: str) -> str: # noqa: ARG002, PLR6301 if not data: - message = 'The `env` context formatting field requires a modifier' + message = "The `env` context formatting field requires a modifier" raise ValueError(message) - env_var, separator, default = data.partition(':') + env_var, separator, default = data.partition(":") if env_var in os.environ: return os.environ[env_var] if not separator: - message = f'Nonexistent environment variable must set a default: {env_var}' + message = f"Nonexistent environment variable must set a default: {env_var}" raise ValueError(message) return default @@ -155,11 +155,11 @@ def get_value(self, key: int | str, args: Sequence[Any], kwargs: Mapping[str, An try: return super().get_value(key, args, kwargs) except KeyError: - message = f'Unknown context field `{key}`' + message = f"Unknown context field `{key}`" raise ValueError(message) from None def format_field(self, value: Any, format_spec: str) -> Any: - formatter, _, data = format_spec.partition(':') + formatter, _, data = format_spec.partition(":") if formatter in self.__formatters: return self.__formatters[formatter](value, data) @@ -168,6 +168,6 @@ def format_field(self, value: Any, format_spec: str) -> Any: def parse(self, format_string: str) -> Iterable: for literal_text, field_name, format_spec, conversion in super().parse(format_string): if field_name in self.__formatters: - yield literal_text, field_name, f'{field_name}:{format_spec}', conversion + yield literal_text, field_name, f"{field_name}:{format_spec}", conversion else: yield literal_text, field_name, format_spec, conversion diff --git a/backend/src/hatchling/utils/fs.py b/backend/src/hatchling/utils/fs.py index b76680467..d7413006c 100644 --- a/backend/src/hatchling/utils/fs.py +++ b/backend/src/hatchling/utils/fs.py @@ -20,7 +20,7 @@ def locate_file(root: str, file_name: str, *, boundary: str | None = None) -> st def path_to_uri(path: str) -> str: - if os.sep == '/': - return f'file://{os.path.abspath(path).replace(" ", "%20")}' + if os.sep == "/": + return f"file://{os.path.abspath(path).replace(' ', '%20')}" - return f'file:///{os.path.abspath(path).replace(" ", "%20").replace(os.sep, "/")}' + return f"file:///{os.path.abspath(path).replace(' ', '%20').replace(os.sep, '/')}" diff --git a/backend/src/hatchling/version/core.py b/backend/src/hatchling/version/core.py index b56fd3733..96aa1326c 100644 --- a/backend/src/hatchling/version/core.py +++ b/backend/src/hatchling/version/core.py @@ -20,10 +20,10 @@ def __init__(self, root: str, relative_path: str) -> None: def read(self, *, pattern: str | bool) -> str: if not os.path.isfile(self.__path): - message = f'file does not exist: {self.__relative_path}' + message = f"file does not exist: {self.__relative_path}" raise OSError(message) - with open(self.__path, encoding='utf-8') as f: + with open(self.__path, encoding="utf-8") as f: contents = f.read() if not pattern or pattern is True: @@ -31,21 +31,21 @@ def read(self, *, pattern: str | bool) -> str: match = re.search(pattern, contents, flags=re.MULTILINE) if not match: - message = f'unable to parse the version from the file: {self.__relative_path}' + message = f"unable to parse the version from the file: {self.__relative_path}" raise ValueError(message) groups = match.groupdict() - if 'version' not in groups: - message = 'no group named `version` was defined in the pattern' + if "version" not in groups: + message = "no group named `version` was defined in the pattern" raise ValueError(message) - self.__cached_read_data = groups['version'], contents, match.span('version') + self.__cached_read_data = groups["version"], contents, match.span("version") return self.__cached_read_data[0] def set_version(self, version: str) -> None: _old_version, file_contents, (start, end) = self.__cached_read_data # type: ignore[misc] - with open(self.__path, 'w', encoding='utf-8') as f: - f.write(f'{file_contents[:start]}{version}{file_contents[end:]}') + with open(self.__path, "w", encoding="utf-8") as f: + f.write(f"{file_contents[:start]}{version}{file_contents[end:]}") def write(self, version: str, template: str = DEFAULT_TEMPLATE) -> None: template = template or DEFAULT_TEMPLATE @@ -54,5 +54,5 @@ def write(self, version: str, template: str = DEFAULT_TEMPLATE) -> None: if not os.path.isdir(parent_dir): os.makedirs(parent_dir) - with open(self.__path, 'w', encoding='utf-8') as f: + with open(self.__path, "w", encoding="utf-8") as f: f.write(template.format(version=version)) diff --git a/backend/src/hatchling/version/scheme/plugin/interface.py b/backend/src/hatchling/version/scheme/plugin/interface.py index 6cf12aa5e..dbc5b4a8a 100644 --- a/backend/src/hatchling/version/scheme/plugin/interface.py +++ b/backend/src/hatchling/version/scheme/plugin/interface.py @@ -14,7 +14,7 @@ class VersionSchemeInterface(ABC): # no cov class SpecialVersionScheme(VersionSchemeInterface): - PLUGIN_NAME = 'special' + PLUGIN_NAME = "special" ... ``` @@ -30,7 +30,7 @@ def hatch_register_version_scheme(): ``` """ - PLUGIN_NAME = '' + PLUGIN_NAME = "" """The name used for selection.""" def __init__(self, root: str, config: dict) -> None: @@ -67,11 +67,11 @@ def validate_bump(self) -> bool: from hatchling.utils.constants import VersionEnvVars if VersionEnvVars.VALIDATE_BUMP in os.environ: - return os.environ[VersionEnvVars.VALIDATE_BUMP] not in {'false', '0'} + return os.environ[VersionEnvVars.VALIDATE_BUMP] not in {"false", "0"} - validate_bump = self.config.get('validate-bump', True) + validate_bump = self.config.get("validate-bump", True) if not isinstance(validate_bump, bool): - message = 'option `validate-bump` must be a boolean' + message = "option `validate-bump` must be a boolean" raise TypeError(message) return validate_bump diff --git a/backend/src/hatchling/version/scheme/standard.py b/backend/src/hatchling/version/scheme/standard.py index af63ce4c5..479a39a07 100644 --- a/backend/src/hatchling/version/scheme/standard.py +++ b/backend/src/hatchling/version/scheme/standard.py @@ -13,7 +13,7 @@ class StandardScheme(VersionSchemeInterface): See https://peps.python.org/pep-0440/ """ - PLUGIN_NAME = 'standard' + PLUGIN_NAME = "standard" def update( self, @@ -24,20 +24,20 @@ def update( from packaging.version import Version original = Version(original_version) - versions = desired_version.split(',') + versions = desired_version.split(",") for version in versions: - if version == 'release': + if version == "release": reset_version_parts(original, release=original.release) - elif version == 'major': + elif version == "major": reset_version_parts(original, release=update_release(original, [original.major + 1])) - elif version == 'minor': + elif version == "minor": reset_version_parts(original, release=update_release(original, [original.major, original.minor + 1])) - elif version in {'micro', 'patch', 'fix'}: + elif version in {"micro", "patch", "fix"}: reset_version_parts( original, release=update_release(original, [original.major, original.minor, original.micro + 1]) ) - elif version in {'a', 'b', 'c', 'rc', 'alpha', 'beta', 'pre', 'preview'}: + elif version in {"a", "b", "c", "rc", "alpha", "beta", "pre", "preview"}: phase, number = parse_letter_version(version, 0) if original.pre: current_phase, current_number = parse_letter_version(*original.pre) @@ -45,20 +45,20 @@ def update( number = current_number + 1 reset_version_parts(original, pre=(phase, number)) - elif version in {'post', 'rev', 'r'}: + elif version in {"post", "rev", "r"}: number = 0 if original.post is None else original.post + 1 reset_version_parts(original, post=parse_letter_version(version, number)) - elif version == 'dev': + elif version == "dev": number = 0 if original.dev is None else original.dev + 1 reset_version_parts(original, dev=(version, number)) else: if len(versions) > 1: - message = 'Cannot specify multiple update operations with an explicit version' + message = "Cannot specify multiple update operations with an explicit version" raise ValueError(message) next_version = Version(version) if self.validate_bump and next_version <= original: - message = f'Version `{version}` is not higher than the original version `{original_version}`' + message = f"Version `{version}` is not higher than the original version `{original_version}`" raise ValueError(message) return str(next_version) @@ -70,7 +70,7 @@ def reset_version_parts(version: Version, **kwargs: Any) -> None: # https://github.com/pypa/packaging/blob/20.9/packaging/version.py#L301-L310 internal_version = version._version # noqa: SLF001 parts: dict[str, Any] = {} - ordered_part_names = ('epoch', 'release', 'pre', 'post', 'dev', 'local') + ordered_part_names = ("epoch", "release", "pre", "post", "dev", "local") reset = False for part_name in ordered_part_names: diff --git a/backend/src/hatchling/version/source/code.py b/backend/src/hatchling/version/source/code.py index dc708aee7..a1e112156 100644 --- a/backend/src/hatchling/version/source/code.py +++ b/backend/src/hatchling/version/source/code.py @@ -6,40 +6,40 @@ class CodeSource(VersionSourceInterface): - PLUGIN_NAME = 'code' + PLUGIN_NAME = "code" def get_version_data(self) -> dict: import sys from importlib.util import module_from_spec, spec_from_file_location - relative_path = self.config.get('path') + relative_path = self.config.get("path") if not relative_path: - message = 'option `path` must be specified' + message = "option `path` must be specified" raise ValueError(message) if not isinstance(relative_path, str): - message = 'option `path` must be a string' + message = "option `path` must be a string" raise TypeError(message) path = os.path.normpath(os.path.join(self.root, relative_path)) if not os.path.isfile(path): - message = f'file does not exist: {relative_path}' + message = f"file does not exist: {relative_path}" raise OSError(message) - expression = self.config.get('expression') or '__version__' + expression = self.config.get("expression") or "__version__" if not isinstance(expression, str): - message = 'option `expression` must be a string' + message = "option `expression` must be a string" raise TypeError(message) - search_paths = self.config.get('search-paths', []) + search_paths = self.config.get("search-paths", []) if not isinstance(search_paths, list): - message = 'option `search-paths` must be an array' + message = "option `search-paths` must be an array" raise TypeError(message) absolute_search_paths = [] for i, search_path in enumerate(search_paths, 1): if not isinstance(search_path, str): - message = f'entry #{i} of option `search-paths` must be a string' + message = f"entry #{i} of option `search-paths` must be a string" raise TypeError(message) absolute_search_paths.append(os.path.normpath(os.path.join(self.root, search_path))) @@ -57,8 +57,8 @@ def get_version_data(self) -> dict: # Execute the expression to determine the version version = eval(expression, vars(module)) # noqa: S307 - return {'version': version} + return {"version": version} - def set_version(self, version: str, version_data: dict) -> None: # noqa: ARG002, PLR6301 - message = 'Cannot rewrite loaded code' + def set_version(self, version: str, version_data: dict) -> None: + message = "Cannot rewrite loaded code" raise NotImplementedError(message) diff --git a/backend/src/hatchling/version/source/env.py b/backend/src/hatchling/version/source/env.py index b46389437..53b1cd7e7 100644 --- a/backend/src/hatchling/version/source/env.py +++ b/backend/src/hatchling/version/source/env.py @@ -6,24 +6,24 @@ class EnvSource(VersionSourceInterface): - PLUGIN_NAME = 'env' + PLUGIN_NAME = "env" def get_version_data(self) -> dict: - variable = self.config.get('variable', '') + variable = self.config.get("variable", "") if not variable: - message = 'option `variable` must be specified' + message = "option `variable` must be specified" raise ValueError(message) if not isinstance(variable, str): - message = 'option `variable` must be a string' + message = "option `variable` must be a string" raise TypeError(message) if variable not in os.environ: - message = f'environment variable `{variable}` is not set' + message = f"environment variable `{variable}` is not set" raise RuntimeError(message) - return {'version': os.environ[variable]} + return {"version": os.environ[variable]} - def set_version(self, version: str, version_data: dict) -> None: # noqa: ARG002, PLR6301 - message = 'Cannot set environment variables' + def set_version(self, version: str, version_data: dict) -> None: + message = "Cannot set environment variables" raise NotImplementedError(message) diff --git a/backend/src/hatchling/version/source/plugin/interface.py b/backend/src/hatchling/version/source/plugin/interface.py index 4f069ebc2..baf3d1d0d 100644 --- a/backend/src/hatchling/version/source/plugin/interface.py +++ b/backend/src/hatchling/version/source/plugin/interface.py @@ -12,7 +12,7 @@ class VersionSourceInterface(ABC): # no cov class SpecialVersionSource(VersionSourceInterface): - PLUGIN_NAME = 'special' + PLUGIN_NAME = "special" ... ``` @@ -28,7 +28,7 @@ def hatch_register_version_source(): ``` """ - PLUGIN_NAME = '' + PLUGIN_NAME = "" """The name used for selection.""" def __init__(self, root: str, config: dict) -> None: diff --git a/backend/src/hatchling/version/source/regex.py b/backend/src/hatchling/version/source/regex.py index 9bb878d09..7bca0ff46 100644 --- a/backend/src/hatchling/version/source/regex.py +++ b/backend/src/hatchling/version/source/regex.py @@ -3,27 +3,27 @@ class RegexSource(VersionSourceInterface): - PLUGIN_NAME = 'regex' + PLUGIN_NAME = "regex" def get_version_data(self) -> dict: - relative_path = self.config.get('path', '') + relative_path = self.config.get("path", "") if not relative_path: - message = 'option `path` must be specified' + message = "option `path` must be specified" raise ValueError(message) if not isinstance(relative_path, str): - message = 'option `path` must be a string' + message = "option `path` must be a string" raise TypeError(message) - pattern = self.config.get('pattern', '') + pattern = self.config.get("pattern", "") if not isinstance(pattern, str): - message = 'option `pattern` must be a string' + message = "option `pattern` must be a string" raise TypeError(message) version_file = VersionFile(self.root, relative_path) version = version_file.read(pattern=pattern) - return {'version': version, 'version_file': version_file} + return {"version": version, "version_file": version_file} def set_version(self, version: str, version_data: dict) -> None: # noqa: PLR6301 - version_data['version_file'].set_version(version) + version_data["version_file"].set_version(version) diff --git a/backend/tests/downstream/integrate.py b/backend/tests/downstream/integrate.py index ddc689945..aeb396c34 100644 --- a/backend/tests/downstream/integrate.py +++ b/backend/tests/downstream/integrate.py @@ -17,7 +17,7 @@ from virtualenv import cli_run HERE = os.path.dirname(os.path.abspath(__file__)) -ON_WINDOWS = platform.system() == 'Windows' +ON_WINDOWS = platform.system() == "Windows" def handle_remove_readonly(func, path, exc): # no cov @@ -51,10 +51,10 @@ def __exit__(self, exc_type, exc_value, traceback): def python_version_supported(project_config): - requires_python = project_config['project'].get('requires-python', '') + requires_python = project_config["project"].get("requires-python", "") if requires_python: python_constraint = SpecifierSet(requires_python) - if not python_constraint.contains(str('.'.join(map(str, sys.version_info[:2])))): + if not python_constraint.contains(str(".".join(map(str, sys.version_info[:2])))): return False return True @@ -62,7 +62,7 @@ def python_version_supported(project_config): def download_file(url, file_name): response = requests.get(url, stream=True, timeout=20) - with open(file_name, 'wb') as f: + with open(file_name, "wb") as f: for chunk in response.iter_content(16384): f.write(chunk) @@ -79,109 +79,109 @@ def temp_dir(): def get_venv_exe_dir(venv_dir): - exe_dir = os.path.join(venv_dir, 'Scripts' if ON_WINDOWS else 'bin') + exe_dir = os.path.join(venv_dir, "Scripts" if ON_WINDOWS else "bin") if os.path.isdir(exe_dir): return exe_dir # PyPy if ON_WINDOWS: - exe_dir = os.path.join(venv_dir, 'bin') + exe_dir = os.path.join(venv_dir, "bin") if os.path.isdir(exe_dir): return exe_dir - message = f'Unable to locate executables directory within: {venv_dir}' + message = f"Unable to locate executables directory within: {venv_dir}" raise OSError(message) # Debian - if os.path.isdir(os.path.join(venv_dir, 'local')): - exe_dir = os.path.join(venv_dir, 'local', 'bin') + if os.path.isdir(os.path.join(venv_dir, "local")): + exe_dir = os.path.join(venv_dir, "local", "bin") if os.path.isdir(exe_dir): return exe_dir - message = f'Unable to locate executables directory within: {venv_dir}' + message = f"Unable to locate executables directory within: {venv_dir}" raise OSError(message) - message = f'Unable to locate executables directory within: {venv_dir}' + message = f"Unable to locate executables directory within: {venv_dir}" raise OSError(message) def main(): original_backend_path = os.path.dirname(os.path.dirname(HERE)) with temp_dir() as links_dir, temp_dir() as build_dir: - print('<<<<< Copying backend >>>>>') - backend_path = os.path.join(build_dir, 'backend') + print("<<<<< Copying backend >>>>>") + backend_path = os.path.join(build_dir, "backend") shutil.copytree(original_backend_path, backend_path) # Increment the minor version - version_file = os.path.join(backend_path, 'src', 'hatchling', '__about__.py') - with open(version_file, encoding='utf-8') as f: + version_file = os.path.join(backend_path, "src", "hatchling", "__about__.py") + with open(version_file, encoding="utf-8") as f: lines = f.readlines() for i, line in enumerate(lines): - if line.startswith('__version__'): - version = line.strip().split(' = ')[1].strip('\'"') - version_parts = version.split('.') + if line.startswith("__version__"): + version = line.strip().split(" = ")[1].strip("'\"") + version_parts = version.split(".") version_parts[1] = str(int(version_parts[1]) + 1) - lines[i] = line.replace(version, '.'.join(version_parts)) + lines[i] = line.replace(version, ".".join(version_parts)) break else: - message = 'No version found' + message = "No version found" raise ValueError(message) - with open(version_file, 'w', encoding='utf-8') as f: + with open(version_file, "w", encoding="utf-8") as f: f.writelines(lines) - print('<<<<< Building backend >>>>>') - subprocess.check_call([sys.executable, '-m', 'build', '--wheel', '-o', links_dir, backend_path]) + print("<<<<< Building backend >>>>>") + subprocess.check_call([sys.executable, "-m", "build", "--wheel", "-o", links_dir, backend_path]) subprocess.check_call([ sys.executable, - '-m', - 'pip', - 'download', - '-q', - '--disable-pip-version-check', - '-d', + "-m", + "pip", + "download", + "-q", + "--disable-pip-version-check", + "-d", links_dir, os.path.join(links_dir, os.listdir(links_dir)[0]), ]) constraints = [] - constraints_file = os.path.join(build_dir, 'constraints.txt') - with open(constraints_file, 'w', encoding='utf-8') as f: - f.write('\n'.join(constraints)) + constraints_file = os.path.join(build_dir, "constraints.txt") + with open(constraints_file, "w", encoding="utf-8") as f: + f.write("\n".join(constraints)) for project in os.listdir(HERE): project_dir = os.path.join(HERE, project) if not os.path.isdir(project_dir): continue - print(f'<<<<< Project: {project} >>>>>') + print(f"<<<<< Project: {project} >>>>>") project_config = {} - potential_project_file = os.path.join(project_dir, 'pyproject.toml') + potential_project_file = os.path.join(project_dir, "pyproject.toml") # Not yet ported if os.path.isfile(potential_project_file): - with open(potential_project_file, encoding='utf-8') as f: + with open(potential_project_file, encoding="utf-8") as f: project_config.update(tomli.loads(f.read())) if not python_version_supported(project_config): - print('--> Unsupported version of Python, skipping') + print("--> Unsupported version of Python, skipping") continue - with open(os.path.join(project_dir, 'data.json'), encoding='utf-8') as f: + with open(os.path.join(project_dir, "data.json"), encoding="utf-8") as f: test_data = json.loads(f.read()) with temp_dir() as d: - if 'repo_url' in test_data: - print('--> Cloning repository') - repo_dir = os.path.join(d, 'repo') - subprocess.check_call(['git', 'clone', '-q', '--depth', '1', test_data['repo_url'], repo_dir]) + if "repo_url" in test_data: + print("--> Cloning repository") + repo_dir = os.path.join(d, "repo") + subprocess.check_call(["git", "clone", "-q", "--depth", "1", test_data["repo_url"], repo_dir]) else: - archive_name = f'{project}.zip' + archive_name = f"{project}.zip" archive_path = os.path.join(d, archive_name) - print('--> Downloading archive') - download_file(test_data['archive_url'], archive_path) + print("--> Downloading archive") + download_file(test_data["archive_url"], archive_path) with ZipFile(archive_path) as zip_file: zip_file.extractall(d) @@ -189,74 +189,74 @@ def main(): entries.remove(archive_name) repo_dir = os.path.join(d, entries[0]) - project_file = os.path.join(repo_dir, 'pyproject.toml') + project_file = os.path.join(repo_dir, "pyproject.toml") if project_config: shutil.copyfile(potential_project_file, project_file) else: if not os.path.isfile(project_file): - sys.exit('--> Missing file: pyproject.toml') + sys.exit("--> Missing file: pyproject.toml") - with open(project_file, encoding='utf-8') as f: + with open(project_file, encoding="utf-8") as f: project_config.update(tomli.loads(f.read())) - for requirement in project_config.get('build-system', {}).get('requires', []): - if Requirement(requirement).name == 'hatchling': + for requirement in project_config.get("build-system", {}).get("requires", []): + if Requirement(requirement).name == "hatchling": break else: - sys.exit('--> Field `build-system.requires` must specify `hatchling` as a requirement') + sys.exit("--> Field `build-system.requires` must specify `hatchling` as a requirement") if not python_version_supported(project_config): - print('--> Unsupported version of Python, skipping') + print("--> Unsupported version of Python, skipping") continue - for file_name in ('MANIFEST.in', 'setup.cfg', 'setup.py'): + for file_name in ("MANIFEST.in", "setup.cfg", "setup.py"): possible_path = os.path.join(repo_dir, file_name) if os.path.isfile(possible_path): os.remove(possible_path) - venv_dir = os.path.join(d, '.venv') - print('--> Creating virtual environment') - cli_run([venv_dir, '--no-download', '--no-periodic-update']) + venv_dir = os.path.join(d, ".venv") + print("--> Creating virtual environment") + cli_run([venv_dir, "--no-download", "--no-periodic-update"]) - env_vars = dict(test_data.get('env_vars', {})) - env_vars['VIRTUAL_ENV'] = venv_dir - env_vars['PATH'] = f'{get_venv_exe_dir(venv_dir)}{os.pathsep}{os.environ["PATH"]}' - env_vars['PIP_CONSTRAINT'] = constraints_file - with EnvVars(env_vars, ignore=('__PYVENV_LAUNCHER__', 'PYTHONHOME')): - print('--> Installing project') + env_vars = dict(test_data.get("env_vars", {})) + env_vars["VIRTUAL_ENV"] = venv_dir + env_vars["PATH"] = f"{get_venv_exe_dir(venv_dir)}{os.pathsep}{os.environ['PATH']}" + env_vars["PIP_CONSTRAINT"] = constraints_file + with EnvVars(env_vars, ignore=("__PYVENV_LAUNCHER__", "PYTHONHOME")): + print("--> Installing project") subprocess.check_call([ - shutil.which('pip'), - 'install', - '-q', - '--disable-pip-version-check', - '--find-links', + shutil.which("pip"), + "install", + "-q", + "--disable-pip-version-check", + "--find-links", links_dir, - '--no-deps', + "--no-deps", repo_dir, ]) - print('--> Installing dependencies') + print("--> Installing dependencies") subprocess.check_call([ - shutil.which('pip'), - 'install', - '-q', - '--disable-pip-version-check', + shutil.which("pip"), + "install", + "-q", + "--disable-pip-version-check", repo_dir, ]) - print('--> Testing package') - for statement in test_data['statements']: - subprocess.check_call([shutil.which('python'), '-c', statement]) + print("--> Testing package") + for statement in test_data["statements"]: + subprocess.check_call([shutil.which("python"), "-c", statement]) - scripts = project_config['project'].get('scripts', {}) + scripts = project_config["project"].get("scripts", {}) if scripts: - print('--> Testing scripts') + print("--> Testing scripts") for script in scripts: if not shutil.which(script): - sys.exit(f'--> Could not locate script: {script}') + sys.exit(f"--> Could not locate script: {script}") - print('--> Success!') + print("--> Success!") -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/docs/.hooks/expand_blocks.py b/docs/.hooks/expand_blocks.py index c34f94a82..8bd37c512 100644 --- a/docs/.hooks/expand_blocks.py +++ b/docs/.hooks/expand_blocks.py @@ -8,7 +8,7 @@ re.MULTILINE, ) _config_example_regex = re.compile( - r'^( *)((`{3,})toml\b.*) config-example\n([\s\S]+?)\n\1\3$', + r"^( *)((`{3,})toml\b.*) config-example\n([\s\S]+?)\n\1\3$", re.MULTILINE, ) @@ -18,30 +18,30 @@ def _code_tab_replace(m): return f"""\ {indent}=== ":octicons-file-code-16: {title}" {indent} {fence_start} -{textwrap.indent(content, ' ')} +{textwrap.indent(content, " ")} {indent} {fence_end} """ def _config_example_replace(m): indent, fence_start, fence_end, content = m.groups() - content_without = re.sub(r' *\[tool.hatch\]\n', '', content.replace('[tool.hatch.', '[')) + content_without = re.sub(r" *\[tool.hatch\]\n", "", content.replace("[tool.hatch.", "[")) return f"""\ {indent}=== ":octicons-file-code-16: pyproject.toml" {indent} {fence_start} -{textwrap.indent(content, ' ')} +{textwrap.indent(content, " ")} {indent} {fence_end} {indent}=== ":octicons-file-code-16: hatch.toml" {indent} {fence_start} -{textwrap.indent(content_without, ' ')} +{textwrap.indent(content_without, " ")} {indent} {fence_end} """ class ExpandedBlocksPreprocessor(Preprocessor): def run(self, lines): # noqa: PLR6301 - markdown = '\n'.join(lines) + markdown = "\n".join(lines) markdown = _config_example_regex.sub(_config_example_replace, markdown) markdown = _code_tab_regex.sub(_code_tab_replace, markdown) return markdown.splitlines() diff --git a/docs/.hooks/inject_version.py b/docs/.hooks/inject_version.py index 11d8d0c35..1ff7c44ae 100644 --- a/docs/.hooks/inject_version.py +++ b/docs/.hooks/inject_version.py @@ -4,7 +4,7 @@ from markdown.preprocessors import Preprocessor -MARKER = '' +MARKER = "" SEMVER_PARTS = 3 @@ -13,17 +13,17 @@ def get_latest_version(): env = dict(os.environ) # Ignore the current documentation environment so that the version # command can execute as usual in the default build environment - env.pop('HATCH_ENV_ACTIVE', None) + env.pop("HATCH_ENV_ACTIVE", None) - output = subprocess.check_output(['hatch', '--no-color', 'version'], env=env).decode('utf-8').strip() # noqa: S607 + output = subprocess.check_output(["hatch", "--no-color", "version"], env=env).decode("utf-8").strip() # noqa: S607 - version = output.replace('dev', '') - parts = list(map(int, version.split('.'))) + version = output.replace("dev", "") + parts = list(map(int, version.split("."))) major, minor, patch = parts[:SEMVER_PARTS] if len(parts) > SEMVER_PARTS: patch -= 1 - return f'{major}.{minor}.{patch}' + return f"{major}.{minor}.{patch}" class VersionInjectionPreprocessor(Preprocessor): diff --git a/docs/.hooks/render_default_test_env.py b/docs/.hooks/render_default_test_env.py index 1a48e8b18..7813d6fbf 100644 --- a/docs/.hooks/render_default_test_env.py +++ b/docs/.hooks/render_default_test_env.py @@ -7,29 +7,29 @@ import tomlkit from markdown.preprocessors import Preprocessor -MARKER_DEPENDENCIES = '' -MARKER_MATRIX = '' -MARKER_SCRIPTS = '' +MARKER_DEPENDENCIES = "" +MARKER_MATRIX = "" +MARKER_SCRIPTS = "" @cache def test_env_config(): - path = os.path.join(os.getcwd(), 'src', 'hatch', 'env', 'internal', 'test.py') - with open(path, encoding='utf-8') as f: + path = os.path.join(os.getcwd(), "src", "hatch", "env", "internal", "test.py") + with open(path, encoding="utf-8") as f: contents = f.read() - value = ''.join(contents.split(' return ')[1].strip().splitlines()) + value = "".join(contents.split(" return ")[1].strip().splitlines()) return literal_eval(value) @cache def get_dependencies_toml(): - env_config = {'dependencies': test_env_config()['dependencies']} - content = tomlkit.dumps({'tool': {'hatch': {'envs': {'hatch-test': env_config}}}}).strip() + env_config = {"dependencies": test_env_config()["dependencies"]} + content = tomlkit.dumps({"tool": {"hatch": {"envs": {"hatch-test": env_config}}}}).strip() # Reload to fix the long array config = tomlkit.loads(content) - config['tool']['hatch']['envs']['hatch-test']['dependencies'].multiline(True) + config["tool"]["hatch"]["envs"]["hatch-test"]["dependencies"].multiline(True) # Reduce indentation content = tomlkit.dumps(config).strip() @@ -38,20 +38,20 @@ def get_dependencies_toml(): @cache def get_matrix_toml(): - env_config = {'matrix': test_env_config()['matrix']} - return tomlkit.dumps({'tool': {'hatch': {'envs': {'hatch-test': env_config}}}}).strip() + env_config = {"matrix": test_env_config()["matrix"]} + return tomlkit.dumps({"tool": {"hatch": {"envs": {"hatch-test": env_config}}}}).strip() @cache def get_scripts_toml(): - env_config = {'scripts': test_env_config()['scripts']} - return tomlkit.dumps({'tool': {'hatch': {'envs': {'hatch-test': env_config}}}}).strip() + env_config = {"scripts": test_env_config()["scripts"]} + return tomlkit.dumps({"tool": {"hatch": {"envs": {"hatch-test": env_config}}}}).strip() class TestEnvDefaultsPreprocessor(Preprocessor): def run(self, lines): # noqa: PLR6301 return ( - '\n'.join(lines) + "\n".join(lines) .replace(MARKER_DEPENDENCIES, get_dependencies_toml()) .replace(MARKER_MATRIX, get_matrix_toml()) .replace(MARKER_SCRIPTS, get_scripts_toml()) diff --git a/docs/.hooks/render_ruff_defaults.py b/docs/.hooks/render_ruff_defaults.py index 8f4d0fdaf..748c82cf0 100644 --- a/docs/.hooks/render_ruff_defaults.py +++ b/docs/.hooks/render_ruff_defaults.py @@ -8,18 +8,18 @@ from markdown.preprocessors import Preprocessor -MARKER_VERSION = '' -MARKER_SELECTED_RULES = '' -MARKER_UNSELECTED_RULES = '' -MARKER_STABLE_RULES_COUNT = '' -MARKER_PREVIEW_RULES_COUNT = '' -MARKER_UNSELECTED_RULES_COUNT = '' -MARKER_PER_FILE_IGNORED_RULES = '' -RULE_URLS = {'S': 'https://docs.astral.sh/ruff/rules/#flake8-bandit-s'} +MARKER_VERSION = "" +MARKER_SELECTED_RULES = "" +MARKER_UNSELECTED_RULES = "" +MARKER_STABLE_RULES_COUNT = "" +MARKER_PREVIEW_RULES_COUNT = "" +MARKER_UNSELECTED_RULES_COUNT = "" +MARKER_PER_FILE_IGNORED_RULES = "" +RULE_URLS = {"S": "https://docs.astral.sh/ruff/rules/#flake8-bandit-s"} def read_constants(path: str, start: str) -> dict[str, Any]: - with open(path, encoding='utf-8') as f: + with open(path, encoding="utf-8") as f: lines = f.read().splitlines() for i, line in enumerate(lines): @@ -27,22 +27,22 @@ def read_constants(path: str, start: str) -> dict[str, Any]: block_start = i break else: - message = f'Could not find {start} in {path}' + message = f"Could not find {start} in {path}" raise RuntimeError(message) data = {} - exec('\n'.join(lines[block_start:]), None, data) # noqa: S102 + exec("\n".join(lines[block_start:]), None, data) # noqa: S102 return data def parse_rules(rules: tuple[str, ...]) -> defaultdict[str, list[str]]: selected_rules: defaultdict[str, list[str]] = defaultdict(list) - separator = re.compile(r'^(\D+)(\d+)$') + separator = re.compile(r"^(\D+)(\d+)$") for rule in rules: match = separator.search(rule) if match is None: - message = f'Could not parse rule {rule}' + message = f"Could not parse rule {rule}" raise RuntimeError(message) group, number = match.groups() @@ -52,7 +52,7 @@ def parse_rules(rules: tuple[str, ...]) -> defaultdict[str, list[str]]: def construct_collapsed_markdown_rule_list(text: str, rules: defaultdict[str, list[str]]) -> str: - preview_rule_set = set(ruff_data()['PREVIEW_RULES']) + preview_rule_set = set(ruff_data()["PREVIEW_RULES"]) lines = [f'??? "{text}"'] for group, numbers in sorted(rules.items()): @@ -60,15 +60,15 @@ def construct_collapsed_markdown_rule_list(text: str, rules: defaultdict[str, li parts = [] for number in numbers: - rule = f'{group}{number}' - part = f'[{rule}](https://docs.astral.sh/ruff/rules/{rule})' - if f'{group}{number}' in preview_rule_set: - part += '^P^' + rule = f"{group}{number}" + part = f"[{rule}](https://docs.astral.sh/ruff/rules/{rule})" + if f"{group}{number}" in preview_rule_set: + part += "^P^" parts.append(part) - lines.append(f' - {", ".join(parts)}') + lines.append(f" - {', '.join(parts)}") - return '\n'.join(lines) + return "\n".join(lines) @cache @@ -76,8 +76,8 @@ def ruff_data(): root = os.getcwd() data = {} for path, start in ( - (os.path.join(root, 'src', 'hatch', 'cli', 'fmt', 'core.py'), 'STABLE_RULES'), - (os.path.join(root, 'src', 'hatch', 'env', 'internal', 'static_analysis.py'), 'RUFF_DEFAULT_VERSION'), + (os.path.join(root, "src", "hatch", "cli", "fmt", "core.py"), "STABLE_RULES"), + (os.path.join(root, "src", "hatch", "env", "internal", "static_analysis.py"), "RUFF_DEFAULT_VERSION"), ): data.update(read_constants(path, start)) @@ -86,17 +86,17 @@ def ruff_data(): @cache def get_ruff_version(): - return ruff_data()['RUFF_DEFAULT_VERSION'] + return ruff_data()["RUFF_DEFAULT_VERSION"] @cache def get_stable_rules_count(): - return str(len(ruff_data()['STABLE_RULES'])) + return str(len(ruff_data()["STABLE_RULES"])) @cache def get_preview_rules_count(): - return str(len(ruff_data()['PREVIEW_RULES'])) + return str(len(ruff_data()["PREVIEW_RULES"])) @cache @@ -107,36 +107,36 @@ def get_unselected_rules_count(): @cache def get_selected_rules(): data = ruff_data() - rules = parse_rules(data['STABLE_RULES']) - for group, numbers in parse_rules(data['PREVIEW_RULES']).items(): + rules = parse_rules(data["STABLE_RULES"]) + for group, numbers in parse_rules(data["PREVIEW_RULES"]).items(): rules[group].extend(numbers) - return construct_collapsed_markdown_rule_list('Selected rules', rules) + return construct_collapsed_markdown_rule_list("Selected rules", rules) @cache def get_unselected_rules(): - return construct_collapsed_markdown_rule_list('Unselected rules', parse_rules(UNSELECTED_RULES)) + return construct_collapsed_markdown_rule_list("Unselected rules", parse_rules(UNSELECTED_RULES)) @cache def get_per_file_ignored_rules(): lines = [] - for glob, rules in sorted(ruff_data()['PER_FILE_IGNORED_RULES'].items()): + for glob, rules in sorted(ruff_data()["PER_FILE_IGNORED_RULES"].items()): parts = [] for rule in rules: - url = RULE_URLS.get(rule) or f'https://docs.astral.sh/ruff/rules/{rule}' - parts.append(f'[{rule}]({url})') + url = RULE_URLS.get(rule) or f"https://docs.astral.sh/ruff/rules/{rule}" + parts.append(f"[{rule}]({url})") - lines.append(f'- `{glob}`: {", ".join(parts)}') + lines.append(f"- `{glob}`: {', '.join(parts)}") - return '\n'.join(lines) + return "\n".join(lines) class RuffDefaultsPreprocessor(Preprocessor): def run(self, lines): # noqa: PLR6301 return ( - '\n'.join(lines) + "\n".join(lines) .replace(MARKER_VERSION, get_ruff_version()) .replace(MARKER_STABLE_RULES_COUNT, get_stable_rules_count()) .replace(MARKER_PREVIEW_RULES_COUNT, get_preview_rules_count()) @@ -149,180 +149,193 @@ def run(self, lines): # noqa: PLR6301 UNSELECTED_RULES: tuple[str, ...] = ( - 'AIR001', - 'ANN001', - 'ANN002', - 'ANN003', - 'ANN101', - 'ANN102', - 'ANN201', - 'ANN202', - 'ANN204', - 'ANN205', - 'ANN206', - 'ANN401', - 'B027', - 'C901', - 'COM812', - 'COM819', - 'CPY001', - 'D100', - 'D101', - 'D102', - 'D103', - 'D104', - 'D105', - 'D106', - 'D107', - 'D200', - 'D201', - 'D202', - 'D203', - 'D204', - 'D205', - 'D206', - 'D207', - 'D208', - 'D209', - 'D210', - 'D211', - 'D212', - 'D213', - 'D214', - 'D215', - 'D300', - 'D301', - 'D400', - 'D401', - 'D402', - 'D403', - 'D404', - 'D405', - 'D406', - 'D407', - 'D408', - 'D409', - 'D410', - 'D411', - 'D412', - 'D413', - 'D414', - 'D415', - 'D416', - 'D417', - 'D418', - 'D419', - 'DJ001', - 'DJ003', - 'DJ006', - 'DJ007', - 'DJ008', - 'DJ012', - 'DJ013', - 'E111', - 'E114', - 'E117', - 'E301', - 'E302', - 'E303', - 'E304', - 'E305', - 'E306', - 'E501', - 'ERA001', - 'FBT003', - 'FIX001', - 'FIX002', - 'FIX003', - 'FIX004', - 'FURB101', - 'FURB103', - 'FURB140', - 'ISC001', - 'ISC002', - 'NPY001', - 'NPY002', - 'NPY003', - 'NPY201', - 'PD002', - 'PD003', - 'PD004', - 'PD007', - 'PD008', - 'PD009', - 'PD010', - 'PD011', - 'PD012', - 'PD013', - 'PD015', - 'PD101', - 'PD901', - 'PERF203', - 'PGH001', - 'PGH002', - 'PGH003', - 'PGH004', - 'PLR0904', - 'PLR0911', - 'PLR0912', - 'PLR0913', - 'PLR0914', - 'PLR0915', - 'PLR0916', - 'PLR0917', - 'PLR1701', - 'PLR1702', - 'PLR1706', - 'PT004', - 'PT005', - 'PTH100', - 'PTH101', - 'PTH102', - 'PTH103', - 'PTH104', - 'PTH105', - 'PTH106', - 'PTH107', - 'PTH108', - 'PTH109', - 'PTH110', - 'PTH111', - 'PTH112', - 'PTH113', - 'PTH114', - 'PTH115', - 'PTH116', - 'PTH117', - 'PTH118', - 'PTH119', - 'PTH120', - 'PTH121', - 'PTH122', - 'PTH123', - 'PTH124', - 'PTH201', - 'PTH202', - 'PTH203', - 'PTH204', - 'PTH205', - 'PTH206', - 'PTH207', - 'Q000', - 'Q001', - 'Q002', - 'Q003', - 'Q004', - 'RET501', - 'RET502', - 'RUF011', - 'RUF200', - 'S404', - 'S410', - 'S603', - 'SIM401', - 'TD001', - 'TD002', - 'TD003', - 'TRY200', - 'W191', + "AIR001", + "AIR002", + "AIR301", + "AIR302", + "AIR311", + "AIR312", + "ANN001", + "ANN002", + "ANN003", + "ANN101", + "ANN102", + "ANN201", + "ANN202", + "ANN204", + "ANN205", + "ANN206", + "ANN401", + "B027", + "C901", + "COM812", + "COM819", + "CPY001", + "D100", + "D101", + "D102", + "D103", + "D104", + "D105", + "D106", + "D107", + "D200", + "D201", + "D202", + "D203", + "D204", + "D205", + "D206", + "D207", + "D208", + "D209", + "D210", + "D211", + "D212", + "D213", + "D214", + "D215", + "D300", + "D301", + "D400", + "D401", + "D402", + "D403", + "D404", + "D405", + "D406", + "D407", + "D408", + "D409", + "D410", + "D411", + "D412", + "D413", + "D414", + "D415", + "D416", + "D417", + "D418", + "D419", + "DJ001", + "DJ003", + "DJ006", + "DJ007", + "DJ008", + "DJ012", + "DJ013", + "E111", + "E114", + "E117", + "E301", + "E302", + "E303", + "E304", + "E305", + "E306", + "E501", + "E999", + "ERA001", + "FBT003", + "FIX001", + "FIX002", + "FIX003", + "FIX004", + "FURB101", + "FURB103", + "FURB140", + "ISC001", + "ISC002", + "NPY001", + "NPY002", + "NPY003", + "NPY201", + "PD002", + "PD003", + "PD004", + "PD007", + "PD008", + "PD009", + "PD010", + "PD011", + "PD012", + "PD013", + "PD015", + "PD101", + "PD901", + "PERF203", + "PGH001", + "PGH002", + "PGH003", + "PGH004", + "PLR0904", + "PLR0911", + "PLR0912", + "PLR0913", + "PLR0914", + "PLR0915", + "PLR0916", + "PLR0917", + "PLR1701", + "PLR1702", + "PLR1706", + "PT004", + "PT005", + "PTH100", + "PTH101", + "PTH102", + "PTH103", + "PTH104", + "PTH105", + "PTH106", + "PTH107", + "PTH108", + "PTH109", + "PTH110", + "PTH111", + "PTH112", + "PTH113", + "PTH114", + "PTH115", + "PTH116", + "PTH117", + "PTH118", + "PTH119", + "PTH120", + "PTH121", + "PTH122", + "PTH123", + "PTH124", + "PTH201", + "PTH202", + "PTH203", + "PTH204", + "PTH205", + "PTH206", + "PTH207", + "PTH208", + "PTH210", + "PTH211", + "Q000", + "Q001", + "Q002", + "Q003", + "Q004", + "RET501", + "RET502", + "RUF011", + "RUF035", + "RUF200", + "S320", + "S404", + "S410", + "S603", + "SIM401", + "TD001", + "TD002", + "TD003", + "TRY200", + "UP027", + "UP038", + "W191", ) diff --git a/docs/.hooks/title_from_content.py b/docs/.hooks/title_from_content.py index f93df822d..baa3be693 100644 --- a/docs/.hooks/title_from_content.py +++ b/docs/.hooks/title_from_content.py @@ -3,11 +3,11 @@ def on_page_markdown( page, **kwargs, # noqa: ARG001 ): - if 'title' in page.meta: + if "title" in page.meta: return first_line = markdown.strip().splitlines()[0] - if first_line.startswith('# '): - title = first_line[2:].split(' # {:', maxsplit=1)[0].strip() - page.meta['title'] = title - page.meta['social'] = {'cards_layout_options': {'title': title}} + if first_line.startswith("# "): + title = first_line[2:].split(" # {:", maxsplit=1)[0].strip() + page.meta["title"] = title + page.meta["social"] = {"cards_layout_options": {"title": title}} diff --git a/docs/history/hatch.md b/docs/history/hatch.md index 54d0f0f69..d6599242d 100644 --- a/docs/history/hatch.md +++ b/docs/history/hatch.md @@ -15,6 +15,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), ***Added:*** +- Upgrade Ruff to 0.13.2 - The `version` and `project metadata` commands now support projects that do not use Hatchling as the build backend - The `version` command accepts a `--force` option, allowing for downgrades when an explicit version number is given. - Build environments can now be configured, the default build environment is `hatch-build` diff --git a/release/macos/build_pkg.py b/release/macos/build_pkg.py index bcb4dcde9..6ec6bdf6f 100644 --- a/release/macos/build_pkg.py +++ b/release/macos/build_pkg.py @@ -15,9 +15,9 @@ from tempfile import TemporaryDirectory REPO_DIR = Path.cwd() -ASSETS_DIR = Path(__file__).parent / 'pkg' -IDENTIFIER = 'org.python.hatch' -COMPONENT_PACKAGE_NAME = f'{IDENTIFIER}.pkg' +ASSETS_DIR = Path(__file__).parent / "pkg" +IDENTIFIER = "org.python.hatch" +COMPONENT_PACKAGE_NAME = f"{IDENTIFIER}.pkg" README = """\ @@ -39,9 +39,9 @@ def run_command(command: list[str]) -> None: def main(): parser = argparse.ArgumentParser() - parser.add_argument('directory') - parser.add_argument('--binary', required=True) - parser.add_argument('--version', required=True) + parser.add_argument("directory") + parser.add_argument("--binary", required=True) + parser.add_argument("--version", required=True) args = parser.parse_args() directory = Path(args.directory).absolute() @@ -53,59 +53,59 @@ def main(): temp_dir = Path(d) # This is where we assemble files required for builds - resources_dir = temp_dir / 'resources' - shutil.copytree(str(ASSETS_DIR / 'resources'), str(resources_dir)) + resources_dir = temp_dir / "resources" + shutil.copytree(str(ASSETS_DIR / "resources"), str(resources_dir)) - resources_dir.joinpath('README.html').write_text(README.format(version=version), encoding='utf-8') - shutil.copy2(REPO_DIR / 'LICENSE.txt', resources_dir) + resources_dir.joinpath("README.html").write_text(README.format(version=version), encoding="utf-8") + shutil.copy2(REPO_DIR / "LICENSE.txt", resources_dir) # This is what gets shipped to users starting at / (the root directory) - root_dir = temp_dir / 'root' + root_dir = temp_dir / "root" root_dir.mkdir() # This is where we globally install Hatch. We choose to not offer per-user installs because we can't # find out where the location is and therefore cannot add to PATH usually. For more information, see: # https://github.com/aws/aws-cli/commit/f3c3eb8262786142a1712b6da5a1515ad9dc66c5 - relative_binary_dir = Path('usr', 'local', binary_name, 'bin') + relative_binary_dir = Path("usr", "local", binary_name, "bin") binary_dir = root_dir / relative_binary_dir binary_dir.mkdir(parents=True) shutil.copy2(staged_binary, binary_dir) # This is how we add the installation directory to PATH and is also what Go does, # although there are some caveats: https://apple.stackexchange.com/q/126725 - path_file = root_dir / 'etc' / 'paths.d' / binary_name + path_file = root_dir / "etc" / "paths.d" / binary_name path_file.parent.mkdir(parents=True) - path_file.write_text(f'/{relative_binary_dir}\n', encoding='utf-8') + path_file.write_text(f"/{relative_binary_dir}\n", encoding="utf-8") # This is where we build the intermediate components - components_dir = temp_dir / 'components' + components_dir = temp_dir / "components" components_dir.mkdir() run_command([ - 'pkgbuild', - '--root', + "pkgbuild", + "--root", str(root_dir), - '--identifier', + "--identifier", IDENTIFIER, - '--version', + "--version", version, - '--install-location', - '/', + "--install-location", + "/", str(components_dir / COMPONENT_PACKAGE_NAME), ]) # This is where we build the final artifact - build_dir = temp_dir / 'build' + build_dir = temp_dir / "build" build_dir.mkdir() - product_archive = build_dir / f'{binary_name}-universal.pkg' + product_archive = build_dir / f"{binary_name}-universal.pkg" run_command([ - 'productbuild', - '--distribution', - str(ASSETS_DIR / 'distribution.xml'), - '--resources', + "productbuild", + "--distribution", + str(ASSETS_DIR / "distribution.xml"), + "--resources", str(resources_dir), - '--package-path', + "--package-path", str(components_dir), str(product_archive), ]) @@ -115,5 +115,5 @@ def main(): shutil.copy2(product_archive, directory) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/release/unix/make_scripts_portable.py b/release/unix/make_scripts_portable.py index a82457356..7f5b6627a 100644 --- a/release/unix/make_scripts_portable.py +++ b/release/unix/make_scripts_portable.py @@ -12,12 +12,12 @@ def main(): # https://github.com/indygreg/python-build-standalone/blob/20240415/cpython-unix/build-cpython.sh#L812-L813 portable_shebang = b'#!/bin/sh\n"exec" "$(dirname $0)/%s" "$0" "$@"\n' % interpreter.name.encode() - scripts_dir = Path(sysconfig.get_path('scripts')) + scripts_dir = Path(sysconfig.get_path("scripts")) for script in scripts_dir.iterdir(): if not script.is_file(): continue - with script.open('rb') as f: + with script.open("rb") as f: data = BytesIO() for line in f: # Ignore leading blank lines @@ -25,10 +25,10 @@ def main(): continue # Ignore binaries - if not line.startswith(b'#'): + if not line.startswith(b"#"): break - if line.startswith(b'#!%s' % interpreter.parent): + if line.startswith(b"#!%s" % interpreter.parent): executable = Path(line[2:].rstrip().decode()).resolve() data.write(portable_shebang if executable == interpreter else line) else: @@ -41,9 +41,9 @@ def main(): if not contents: continue - with script.open('wb') as f: + with script.open("wb") as f: f.write(contents) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/release/windows/make_scripts_portable.py b/release/windows/make_scripts_portable.py index 36f5ac2ec..a50ae7463 100644 --- a/release/windows/make_scripts_portable.py +++ b/release/windows/make_scripts_portable.py @@ -11,7 +11,7 @@ from urllib.request import urlopen from zipfile import ZIP_DEFLATED, ZipFile, ZipInfo -LAUNCHERS_URL = 'https://raw.githubusercontent.com/astral-sh/uv/main/crates/uv-trampoline/trampolines' +LAUNCHERS_URL = "https://raw.githubusercontent.com/astral-sh/uv/main/crates/uv-trampoline/trampolines" SCRIPT_TEMPLATE = """\ #!{executable} import re @@ -28,18 +28,18 @@ def select_entry_points(ep, group): def fetch_launcher(launcher_name): - with urlopen(f'{LAUNCHERS_URL}/{launcher_name}') as f: # noqa: S310 + with urlopen(f"{LAUNCHERS_URL}/{launcher_name}") as f: # noqa: S310 return f.read() def main(): interpreters_dir = Path(sys.executable).parent - scripts_dir = Path(sysconfig.get_path('scripts')) + scripts_dir = Path(sysconfig.get_path("scripts")) ep = entry_points() for group, interpreter_name, launcher_name in ( - ('console_scripts', 'python.exe', 'uv-trampoline-x86_64-console.exe'), - ('gui_scripts', 'pythonw.exe', 'uv-trampoline-x86_64-gui.exe'), + ("console_scripts", "python.exe", "uv-trampoline-x86_64-console.exe"), + ("gui_scripts", "pythonw.exe", "uv-trampoline-x86_64-gui.exe"), ): interpreter = interpreters_dir / interpreter_name relative_interpreter_path = relpath(interpreter, scripts_dir) @@ -53,17 +53,17 @@ def main(): # Zipped script with TemporaryDirectory() as td: - zip_path = Path(td) / 'script.zip' - with ZipFile(zip_path, 'w') as zf: + zip_path = Path(td) / "script.zip" + with ZipFile(zip_path, "w") as zf: # Ensure reproducibility - zip_info = ZipInfo('__main__.py', (2020, 2, 2, 0, 0, 0)) + zip_info = ZipInfo("__main__.py", (2020, 2, 2, 0, 0, 0)) zip_info.external_attr = (0o644 & 0xFFFF) << 16 - module, _, attrs = script.value.partition(':') + module, _, attrs = script.value.partition(":") contents = SCRIPT_TEMPLATE.format( executable=relative_interpreter_path, module=module, - import_name=attrs.split('.')[0], + import_name=attrs.split(".")[0], function=attrs, ) zf.writestr(zip_info, contents, compress_type=ZIP_DEFLATED) @@ -71,21 +71,21 @@ def main(): buf.write(zip_path.read_bytes()) # Interpreter path - interpreter_path = relative_interpreter_path.encode('utf-8') + interpreter_path = relative_interpreter_path.encode("utf-8") buf.write(interpreter_path) # Interpreter path length - interpreter_path_length = len(interpreter_path).to_bytes(4, 'little') + interpreter_path_length = len(interpreter_path).to_bytes(4, "little") buf.write(interpreter_path_length) # Magic number - buf.write(b'UVUV') + buf.write(b"UVUV") script_data = buf.getvalue() - script_path = scripts_dir / f'{script.name}.exe' + script_path = scripts_dir / f"{script.name}.exe" script_path.write_bytes(script_data) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/ruff.toml b/ruff.toml index 44c57859c..6d15cbd1e 100644 --- a/ruff.toml +++ b/ruff.toml @@ -5,7 +5,6 @@ exclude = [".git", ".mypy_cache", ".ruff_cache", ".venv", "dist"] [format] preview = true -quote-style = "single" [lint] preview = true diff --git a/ruff_defaults.toml b/ruff_defaults.toml index 9acc585a3..65cbaed91 100644 --- a/ruff_defaults.toml +++ b/ruff_defaults.toml @@ -500,7 +500,6 @@ select = [ "S317", "S318", "S319", - "S320", "S321", "S323", "S324", @@ -573,12 +572,12 @@ select = [ "T100", "T201", "T203", - "TCH001", - "TCH002", - "TCH003", - "TCH004", - "TCH005", - "TCH010", + "TC001", + "TC002", + "TC003", + "TC004", + "TC005", + "TC010", "TD004", "TD005", "TD006", @@ -590,9 +589,9 @@ select = [ "TRY003", "TRY004", "TRY201", + "TRY203", "TRY300", "TRY301", - "TRY302", "TRY400", "TRY401", "UP001", @@ -629,7 +628,6 @@ select = [ "UP035", "UP036", "UP037", - "UP038", "UP039", "UP040", "UP041", diff --git a/scripts/bump.py b/scripts/bump.py index 625f3e422..f78be433f 100644 --- a/scripts/bump.py +++ b/scripts/bump.py @@ -6,35 +6,35 @@ from utils import ROOT, get_latest_release TEMPLATE = ( - '## [{version}](https://github.com/pypa/hatch/releases/tag/{project}-v{version}) - ' - '{year}-{month:02}-{day:02} ## {{: #{project}-v{version} }}' + "## [{version}](https://github.com/pypa/hatch/releases/tag/{project}-v{version}) - " + "{year}-{month:02}-{day:02} ## {{: #{project}-v{version} }}" ) def main(): parser = argparse.ArgumentParser() - parser.add_argument('project', choices=['hatch', 'hatchling']) - parser.add_argument('version') + parser.add_argument("project", choices=["hatch", "hatchling"]) + parser.add_argument("version") args = parser.parse_args() root_dir = project_dir = ROOT - if args.project == 'hatchling': - project_dir = root_dir / 'backend' + if args.project == "hatchling": + project_dir = root_dir / "backend" - history_file = root_dir / 'docs' / 'history' / f'{args.project}.md' + history_file = root_dir / "docs" / "history" / f"{args.project}.md" - if args.project == 'hatchling': + if args.project == "hatchling": process = subprocess.run( # noqa: PLW1510 - ['hatch', 'version', args.version], # noqa: S607 + ["hatch", "version", args.version], # noqa: S607 stdout=subprocess.PIPE, stderr=subprocess.STDOUT, - encoding='utf-8', + encoding="utf-8", cwd=str(project_dir), ) if process.returncode: raise OSError(process.stdout) - new_version = re.search(r'New: (.+)$', process.stdout, re.MULTILINE).group(1) + new_version = re.search(r"New: (.+)$", process.stdout, re.MULTILINE).group(1) else: from hatchling.version.scheme.standard import StandardScheme @@ -45,22 +45,22 @@ def main(): now = datetime.now(timezone.utc) - history_file_lines = history_file.read_text(encoding='utf-8').splitlines() - insertion_index = history_file_lines.index('## Unreleased') + 1 + history_file_lines = history_file.read_text(encoding="utf-8").splitlines() + insertion_index = history_file_lines.index("## Unreleased") + 1 history_file_lines.insert( insertion_index, TEMPLATE.format(project=args.project, version=new_version, year=now.year, month=now.month, day=now.day), ) - history_file_lines.insert(insertion_index, '') - history_file_lines.append('') - history_file.write_text('\n'.join(history_file_lines), encoding='utf-8') + history_file_lines.insert(insertion_index, "") + history_file_lines.append("") + history_file.write_text("\n".join(history_file_lines), encoding="utf-8") for command in ( - ['git', 'add', '--all'], - ['git', 'commit', '-m', f'release {args.project.capitalize()} v{new_version}'], + ["git", "add", "--all"], + ["git", "commit", "-m", f"release {args.project.capitalize()} v{new_version}"], ): subprocess.run(command, check=True) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/scripts/generate_coverage_summary.py b/scripts/generate_coverage_summary.py index 26569f97d..283be29db 100644 --- a/scripts/generate_coverage_summary.py +++ b/scripts/generate_coverage_summary.py @@ -5,49 +5,49 @@ from utils import ROOT PACKAGES = { - 'backend/src/hatchling/': 'hatchling', - 'src/hatch/': 'hatch', - 'tests/': 'tests', + "backend/src/hatchling/": "hatchling", + "src/hatch/": "hatch", + "tests/": "tests", } def main(): - coverage_report = ROOT / 'coverage.xml' - root = etree.fromstring(coverage_report.read_text()) # nosec B320 # noqa: S320 + coverage_report = ROOT / "coverage.xml" + root = etree.fromstring(coverage_report.read_text()) # nosec B320 - raw_package_data = defaultdict(lambda: {'hits': 0, 'misses': 0}) - for package in root.find('packages'): - for module in package.find('classes'): - filename = module.attrib['filename'] + raw_package_data = defaultdict(lambda: {"hits": 0, "misses": 0}) + for package in root.find("packages"): + for module in package.find("classes"): + filename = module.attrib["filename"] for relative_path, package_name in PACKAGES.items(): if filename.startswith(relative_path): data = raw_package_data[package_name] break else: - message = f'unknown package: {module}' + message = f"unknown package: {module}" raise ValueError(message) - for line in module.find('lines'): - if line.attrib['hits'] == '1': - data['hits'] += 1 + for line in module.find("lines"): + if line.attrib["hits"] == "1": + data["hits"] += 1 else: - data['misses'] += 1 + data["misses"] += 1 total_statements_covered = 0 total_statements = 0 coverage_data = {} for package_name, data in sorted(raw_package_data.items()): - statements_covered = data['hits'] - statements = statements_covered + data['misses'] + statements_covered = data["hits"] + statements = statements_covered + data["misses"] total_statements_covered += statements_covered total_statements += statements - coverage_data[package_name] = {'statements_covered': statements_covered, 'statements': statements} - coverage_data['total'] = {'statements_covered': total_statements_covered, 'statements': total_statements} + coverage_data[package_name] = {"statements_covered": statements_covered, "statements": statements} + coverage_data["total"] = {"statements_covered": total_statements_covered, "statements": total_statements} - coverage_summary = ROOT / 'coverage-summary.json' - coverage_summary.write_text(json.dumps(coverage_data, indent=4), encoding='utf-8') + coverage_summary = ROOT / "coverage-summary.json" + coverage_summary.write_text(json.dumps(coverage_data, indent=4), encoding="utf-8") -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/scripts/install_mkdocs_material_insiders.py b/scripts/install_mkdocs_material_insiders.py index 27820462e..bbbd01aa0 100644 --- a/scripts/install_mkdocs_material_insiders.py +++ b/scripts/install_mkdocs_material_insiders.py @@ -2,34 +2,34 @@ import subprocess import sys -TOKEN = os.environ.get('GH_TOKEN_MKDOCS_MATERIAL_INSIDERS', '') -DEP_REF = f'git+https://{TOKEN}@github.com/squidfunk/mkdocs-material-insiders.git' -GIT_REF = 'f2d5b41b2e590baf73ae5f51166d88b233ba96aa' +TOKEN = os.environ.get("GH_TOKEN_MKDOCS_MATERIAL_INSIDERS", "") +DEP_REF = f"git+https://{TOKEN}@github.com/squidfunk/mkdocs-material-insiders.git" +GIT_REF = "f2d5b41b2e590baf73ae5f51166d88b233ba96aa" def main(): if not TOKEN: - print('No token is set, skipping') + print("No token is set, skipping") return - dependency = f'mkdocs-material[imaging] @ {DEP_REF}@{GIT_REF}' + dependency = f"mkdocs-material[imaging] @ {DEP_REF}@{GIT_REF}" try: process = subprocess.Popen( - ['uv', 'pip', 'install', dependency], # noqa: S607 + ["uv", "pip", "install", dependency], # noqa: S607 stdout=subprocess.PIPE, stderr=subprocess.STDOUT, - encoding='utf-8', + encoding="utf-8", ) except Exception as e: # noqa: BLE001 - print(str(e).replace(TOKEN, '*****')) + print(str(e).replace(TOKEN, "*****")) sys.exit(1) with process: - for line in iter(process.stdout.readline, ''): - print(line.replace(TOKEN, '*****'), end='') + for line in iter(process.stdout.readline, ""): + print(line.replace(TOKEN, "*****"), end="") sys.exit(process.returncode) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/scripts/release_github.py b/scripts/release_github.py index 081ea438e..3b26a4db3 100644 --- a/scripts/release_github.py +++ b/scripts/release_github.py @@ -7,20 +7,20 @@ def main(): parser = argparse.ArgumentParser() - parser.add_argument('project', choices=['hatch', 'hatchling']) + parser.add_argument("project", choices=["hatch", "hatchling"]) args = parser.parse_args() version, notes = get_latest_release(args.project) params = urlencode({ - 'title': f'{args.project.capitalize()} v{version}', - 'tag': f'{args.project}-v{version}', - 'body': notes, + "title": f"{args.project.capitalize()} v{version}", + "tag": f"{args.project}-v{version}", + "body": notes, }) - url = f'https://github.com/pypa/hatch/releases/new?{params}' + url = f"https://github.com/pypa/hatch/releases/new?{params}" webbrowser.open_new_tab(url) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/scripts/set_release_version.py b/scripts/set_release_version.py index 85e40d59a..9aba3f85d 100644 --- a/scripts/set_release_version.py +++ b/scripts/set_release_version.py @@ -4,12 +4,12 @@ def main(): - version, _ = get_latest_release('hatch') - parts = version.split('.') + version, _ = get_latest_release("hatch") + parts = version.split(".") - with open(os.environ['GITHUB_ENV'], 'a', encoding='utf-8') as f: - f.write(f'HATCH_DOCS_VERSION={parts[0]}.{parts[1]}\n') + with open(os.environ["GITHUB_ENV"], "a", encoding="utf-8") as f: + f.write(f"HATCH_DOCS_VERSION={parts[0]}.{parts[1]}\n") -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/scripts/update_distributions.py b/scripts/update_distributions.py index 6f3e89de9..174337f50 100644 --- a/scripts/update_distributions.py +++ b/scripts/update_distributions.py @@ -7,36 +7,36 @@ import httpx from utils import ROOT -URL = 'https://raw.githubusercontent.com/ofek/pyapp/master/build.rs' -OUTPUT_FILE = ROOT / 'src' / 'hatch' / 'python' / 'distributions.py' -ARCHES = {('linux', 'x86'): 'i686', ('windows', 'x86_64'): 'amd64', ('windows', 'x86'): 'i386'} +URL = "https://raw.githubusercontent.com/ofek/pyapp/master/build.rs" +OUTPUT_FILE = ROOT / "src" / "hatch" / "python" / "distributions.py" +ARCHES = {("linux", "x86"): "i686", ("windows", "x86_64"): "amd64", ("windows", "x86"): "i386"} # system, architecture, ABI, CPU variant, GIL variant MAX_IDENTIFIER_COMPONENTS = 5 def parse_distributions(contents: str, constant: str): - match = re.search(f'^const {constant}.+?^];$', contents, flags=re.DOTALL | re.MULTILINE) + match = re.search(f"^const {constant}.+?^];$", contents, flags=re.DOTALL | re.MULTILINE) if not match: - message = f'Could not find {constant} in {URL}' + message = f"Could not find {constant} in {URL}" raise ValueError(message) block = match.group(0).replace('",\n', '",') for raw_line in block.splitlines()[1:-1]: line = raw_line.strip() - if not line or line.startswith('//'): + if not line or line.startswith("//"): continue identifier, *data, source = literal_eval(line[:-1]) os, arch = data[:2] - if arch == 'powerpc64': - arch = 'ppc64le' - elif os == 'macos' and arch == 'aarch64': - arch = 'arm64' + if arch == "powerpc64": + arch = "ppc64le" + elif os == "macos" and arch == "aarch64": + arch = "arm64" # Force everything to have the proper number of variants to maintain structure if len(data) != MAX_IDENTIFIER_COMPONENTS: - data.extend(('', '')) + data.extend(("", "")) data[1] = ARCHES.get((os, arch), arch) yield identifier, tuple(data), source @@ -50,33 +50,33 @@ def main(): distributions = defaultdict(list) ordering_data = defaultdict(dict) - for i, distribution_type in enumerate(('DEFAULT_CPYTHON_DISTRIBUTIONS', 'DEFAULT_PYPY_DISTRIBUTIONS')): + for i, distribution_type in enumerate(("DEFAULT_CPYTHON_DISTRIBUTIONS", "DEFAULT_PYPY_DISTRIBUTIONS")): for identifier, data, source in parse_distributions(contents, distribution_type): ordering_data[i][identifier] = None distributions[identifier].append((data, source)) ordered = [identifier for identifiers in ordering_data.values() for identifier in reversed(identifiers)] output = [ - 'from __future__ import annotations', - '', - '# fmt: off', - 'ORDERED_DISTRIBUTIONS: tuple[str, ...] = (', + "from __future__ import annotations", + "", + "# fmt: off", + "ORDERED_DISTRIBUTIONS: tuple[str, ...] = (", ] - output.extend(f' {identifier!r},' for identifier in ordered) - output.extend((')', 'DISTRIBUTIONS: dict[str, dict[tuple[str, ...], str]] = {')) + output.extend(f" {identifier!r}," for identifier in ordered) + output.extend((")", "DISTRIBUTIONS: dict[str, dict[tuple[str, ...], str]] = {")) for identifier, data in distributions.items(): - output.append(f' {identifier!r}: {{') + output.append(f" {identifier!r}: {{") for d, source in data: - output.extend((f' {d!r}:', f' {source!r},')) - output.append(' },') + output.extend((f" {d!r}:", f" {source!r},")) + output.append(" },") - output.extend(('}', '')) - output = '\n'.join(output) + output.extend(("}", "")) + output = "\n".join(output) - with open(OUTPUT_FILE, 'w', encoding='utf-8') as f: + with open(OUTPUT_FILE, "w", encoding="utf-8") as f: f.write(output) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/scripts/update_ruff.py b/scripts/update_ruff.py index 424f4ea6a..d527d9e60 100644 --- a/scripts/update_ruff.py +++ b/scripts/update_ruff.py @@ -77,13 +77,13 @@ def get_lines_until(file_path: Path, marker: str) -> list[str]: - lines = file_path.read_text(encoding='utf-8').splitlines() + lines = file_path.read_text(encoding="utf-8").splitlines() for i, line in enumerate(lines): if line.startswith(marker): block_start = i break else: - message = f'Could not find {marker}: {file_path.relative_to(ROOT)}' + message = f"Could not find {marker}: {file_path.relative_to(ROOT)}" raise ValueError(message) del lines[block_start:] @@ -92,81 +92,81 @@ def get_lines_until(file_path: Path, marker: str) -> list[str]: def main(): process = subprocess.run( # noqa: PLW1510 - [sys.executable, '-m', 'ruff', 'rule', '--all', '--output-format', 'json'], + [sys.executable, "-m", "ruff", "rule", "--all", "--output-format", "json"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, - encoding='utf-8', + encoding="utf-8", cwd=str(ROOT), ) if process.returncode: raise OSError(process.stdout) - data_file = ROOT / 'src' / 'hatch' / 'cli' / 'fmt' / 'core.py' - lines = get_lines_until(data_file, 'STABLE_RULES') + data_file = ROOT / "src" / "hatch" / "cli" / "fmt" / "core.py" + lines = get_lines_until(data_file, "STABLE_RULES") - ignored_pattern = re.compile(f'^({"|".join(UNSELECTED_RULE_PATTERNS)})$') + ignored_pattern = re.compile(f"^({'|'.join(UNSELECTED_RULE_PATTERNS)})$") # https://github.com/astral-sh/ruff/issues/9891#issuecomment-1951403651 - removed_pattern = re.compile(r'^\s*#+\s+(removed|removal)', flags=re.IGNORECASE | re.MULTILINE) + removed_pattern = re.compile(r"^\s*#+\s+(removed|removal)", flags=re.IGNORECASE | re.MULTILINE) stable_rules: set[str] = set() preview_rules: set[str] = set() unselected_rules: set[str] = set() for rule in json.loads(process.stdout): - code = rule['code'] - if ignored_pattern.match(code) or removed_pattern.search(rule['explanation']): + code = rule["code"] + if ignored_pattern.match(code) or removed_pattern.search(rule["explanation"]): unselected_rules.add(code) continue - if rule['preview']: + if rule["preview"]: preview_rules.add(code) else: stable_rules.add(code) - lines.append('STABLE_RULES: tuple[str, ...] = (') - lines.extend(f' {rule!r},' for rule in sorted(stable_rules)) - lines.append(')') + lines.append("STABLE_RULES: tuple[str, ...] = (") + lines.extend(f" {rule!r}," for rule in sorted(stable_rules)) + lines.append(")") - lines.append('PREVIEW_RULES: tuple[str, ...] = (') - lines.extend(f' {rule!r},' for rule in sorted(preview_rules)) - lines.append(')') + lines.append("PREVIEW_RULES: tuple[str, ...] = (") + lines.extend(f" {rule!r}," for rule in sorted(preview_rules)) + lines.append(")") - lines.append('PER_FILE_IGNORED_RULES: dict[str, list[str]] = {') + lines.append("PER_FILE_IGNORED_RULES: dict[str, list[str]] = {") for ignored_glob, ignored_rules in sorted(PER_FILE_IGNORED_RULES.items()): - lines.append(f' {ignored_glob!r}: [') - lines.extend(f' {rule!r},' for rule in sorted(ignored_rules)) - lines.append(' ],') - lines.append('}') + lines.append(f" {ignored_glob!r}: [") + lines.extend(f" {rule!r}," for rule in sorted(ignored_rules)) + lines.append(" ],") + lines.append("}") - lines.append('') - data_file.write_text('\n'.join(lines), encoding='utf-8') + lines.append("") + data_file.write_text("\n".join(lines), encoding="utf-8") - version_file = ROOT / 'src' / 'hatch' / 'env' / 'internal' / 'static_analysis.py' - latest_version = version('ruff') + version_file = ROOT / "src" / "hatch" / "env" / "internal" / "static_analysis.py" + latest_version = version("ruff") version_file.write_text( re.sub( - r'^(RUFF_DEFAULT_VERSION.+=.+\').+?(\')$', - rf'\g<1>{latest_version}\g<2>', - version_file.read_text(encoding='utf-8'), + r"^(RUFF_DEFAULT_VERSION.+=.+\').+?(\')$", + rf"\g<1>{latest_version}\g<2>", + version_file.read_text(encoding="utf-8"), count=1, flags=re.MULTILINE, ), - encoding='utf-8', + encoding="utf-8", ) - data_file = ROOT / 'docs' / '.hooks' / 'render_ruff_defaults.py' - lines = get_lines_until(data_file, 'UNSELECTED_RULES') + data_file = ROOT / "docs" / ".hooks" / "render_ruff_defaults.py" + lines = get_lines_until(data_file, "UNSELECTED_RULES") - lines.append('UNSELECTED_RULES: tuple[str, ...] = (') - lines.extend(f' {rule!r},' for rule in sorted(unselected_rules)) - lines.append(')') + lines.append("UNSELECTED_RULES: tuple[str, ...] = (") + lines.extend(f" {rule!r}," for rule in sorted(unselected_rules)) + lines.append(")") - lines.append('') - data_file.write_text('\n'.join(lines), encoding='utf-8') + lines.append("") + data_file.write_text("\n".join(lines), encoding="utf-8") - print(f'Stable rules: {len(stable_rules)}') - print(f'Preview rules: {len(preview_rules)}') - print(f'Unselected rules: {len(unselected_rules)}') + print(f"Stable rules: {len(stable_rules)}") + print(f"Preview rules: {len(preview_rules)}") + print(f"Unselected rules: {len(unselected_rules)}") -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/scripts/utils.py b/scripts/utils.py index afe8840e7..2c6b87048 100644 --- a/scripts/utils.py +++ b/scripts/utils.py @@ -5,24 +5,24 @@ def get_latest_release(project): - history_file = ROOT / 'docs' / 'history' / f'{project}.md' + history_file = ROOT / "docs" / "history" / f"{project}.md" release_headers = 0 history_file_lines = [] - with history_file.open(encoding='utf-8') as f: + with history_file.open(encoding="utf-8") as f: for line in f: history_file_lines.append(line.rstrip()) - if line.startswith('## '): + if line.startswith("## "): release_headers += 1 if release_headers == 3: # noqa: PLR2004 break - release_lines = history_file_lines[history_file_lines.index('## Unreleased') + 1 : -1] + release_lines = history_file_lines[history_file_lines.index("## Unreleased") + 1 : -1] while True: release_header = release_lines.pop(0) - if release_header.startswith('## '): + if release_header.startswith("## "): break - return re.search(r'\[(.+)\]', release_header).group(1), '\n'.join(release_lines).strip() + return re.search(r"\[(.+)\]", release_header).group(1), "\n".join(release_lines).strip() diff --git a/scripts/validate_history.py b/scripts/validate_history.py index 35f2dc4b6..b349e24f6 100644 --- a/scripts/validate_history.py +++ b/scripts/validate_history.py @@ -4,32 +4,32 @@ from utils import ROOT HEADER_PATTERN = ( - r'^\[([a-z0-9.]+)\]\(https://github\.com/pypa/hatch/releases/tag/({package}-v\1)\)' - r' - [0-9]{{4}}-[0-9]{{2}}-[0-9]{{2}} ## \{{: #\2 \}}$' + r"^\[([a-z0-9.]+)\]\(https://github\.com/pypa/hatch/releases/tag/({package}-v\1)\)" + r" - [0-9]{{4}}-[0-9]{{2}}-[0-9]{{2}} ## \{{: #\2 \}}$" ) def main(): - for package in ('hatch', 'hatchling'): - history_file = ROOT / 'docs' / 'history' / f'{package}.md' + for package in ("hatch", "hatchling"): + history_file = ROOT / "docs" / "history" / f"{package}.md" current_pattern = HEADER_PATTERN.format(package=package) - with history_file.open('r', encoding='utf-8') as f: + with history_file.open("r", encoding="utf-8") as f: for raw_line in f: line = raw_line.strip() if not line: continue - if line.startswith('## '): - _, _, header = line.partition(' ') - if header == 'Unreleased': + if line.startswith("## "): + _, _, header = line.partition(" ") + if header == "Unreleased": continue if not re.search(current_pattern, header): - print('Invalid header:') + print("Invalid header:") print(header) sys.exit(1) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/scripts/write_coverage_summary_report.py b/scripts/write_coverage_summary_report.py index 8fc965d66..067ef4e91 100644 --- a/scripts/write_coverage_summary_report.py +++ b/scripts/write_coverage_summary_report.py @@ -2,48 +2,48 @@ from decimal import ROUND_DOWN, Decimal from pathlib import Path -PRECISION = Decimal('.01') +PRECISION = Decimal(".01") def main(): project_root = Path(__file__).resolve().parent.parent - coverage_summary = project_root / 'coverage-summary.json' + coverage_summary = project_root / "coverage-summary.json" - coverage_data = json.loads(coverage_summary.read_text(encoding='utf-8')) - total_data = coverage_data.pop('total') + coverage_data = json.loads(coverage_summary.read_text(encoding="utf-8")) + total_data = coverage_data.pop("total") lines = [ - '\n', - 'Package | Statements\n', - '--- | ---\n', + "\n", + "Package | Statements\n", + "--- | ---\n", ] for package, data in sorted(coverage_data.items()): - statements_covered = data['statements_covered'] - statements = data['statements'] + statements_covered = data["statements_covered"] + statements = data["statements"] rate = Decimal(statements_covered) / Decimal(statements) * 100 rate = rate.quantize(PRECISION, rounding=ROUND_DOWN) lines.append( - f'{package} | {100 if rate == 100 else rate}% ({statements_covered} / {statements})\n' # noqa: PLR2004 + f"{package} | {100 if rate == 100 else rate}% ({statements_covered} / {statements})\n" # noqa: PLR2004 ) - total_statements_covered = total_data['statements_covered'] - total_statements = total_data['statements'] + total_statements_covered = total_data["statements_covered"] + total_statements = total_data["statements"] total_rate = Decimal(total_statements_covered) / Decimal(total_statements) * 100 total_rate = total_rate.quantize(PRECISION, rounding=ROUND_DOWN) - color = 'ok' if float(total_rate) >= 95 else 'critical' # noqa: PLR2004 - lines.insert(0, f'![Code Coverage](https://img.shields.io/badge/coverage-{total_rate}%25-{color}?style=flat)\n') + color = "ok" if float(total_rate) >= 95 else "critical" # noqa: PLR2004 + lines.insert(0, f"![Code Coverage](https://img.shields.io/badge/coverage-{total_rate}%25-{color}?style=flat)\n") lines.append( - f'**Summary** | {100 if total_rate == 100 else total_rate}% ' # noqa: PLR2004 - f'({total_statements_covered} / {total_statements})\n' + f"**Summary** | {100 if total_rate == 100 else total_rate}% " # noqa: PLR2004 + f"({total_statements_covered} / {total_statements})\n" ) - coverage_report = project_root / 'coverage-report.md' - with coverage_report.open('w', encoding='utf-8') as f: - f.write(''.join(lines)) + coverage_report = project_root / "coverage-report.md" + with coverage_report.open("w", encoding="utf-8") as f: + f.write("".join(lines)) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/src/hatch/__main__.py b/src/hatch/__main__.py index 57193ab16..4b43fbec5 100644 --- a/src/hatch/__main__.py +++ b/src/hatch/__main__.py @@ -1,4 +1,4 @@ -if __name__ == '__main__': +if __name__ == "__main__": from hatch.cli import main main() diff --git a/src/hatch/cli/__init__.py b/src/hatch/cli/__init__.py index 242b7b626..286df6b34 100644 --- a/src/hatch/cli/__init__.py +++ b/src/hatch/cli/__init__.py @@ -30,67 +30,67 @@ @click.group( - context_settings={'help_option_names': ['-h', '--help'], 'max_content_width': 120}, invoke_without_command=True + context_settings={"help_option_names": ["-h", "--help"], "max_content_width": 120}, invoke_without_command=True ) @click.option( - '--env', - '-e', - 'env_name', + "--env", + "-e", + "env_name", envvar=AppEnvVars.ENV, - default='default', - help='The name of the environment to use [env var: `HATCH_ENV`]', + default="default", + help="The name of the environment to use [env var: `HATCH_ENV`]", ) @click.option( - '--project', - '-p', + "--project", + "-p", envvar=ConfigEnvVars.PROJECT, - help='The name of the project to work on [env var: `HATCH_PROJECT`]', + help="The name of the project to work on [env var: `HATCH_PROJECT`]", ) @click.option( - '--verbose', - '-v', + "--verbose", + "-v", envvar=AppEnvVars.VERBOSE, count=True, - help='Increase verbosity (can be used additively) [env var: `HATCH_VERBOSE`]', + help="Increase verbosity (can be used additively) [env var: `HATCH_VERBOSE`]", ) @click.option( - '--quiet', - '-q', + "--quiet", + "-q", envvar=AppEnvVars.QUIET, count=True, - help='Decrease verbosity (can be used additively) [env var: `HATCH_QUIET`]', + help="Decrease verbosity (can be used additively) [env var: `HATCH_QUIET`]", ) @click.option( - '--color/--no-color', + "--color/--no-color", default=None, - help='Whether or not to display colored output (default is auto-detection) [env vars: `FORCE_COLOR`/`NO_COLOR`]', + help="Whether or not to display colored output (default is auto-detection) [env vars: `FORCE_COLOR`/`NO_COLOR`]", ) @click.option( - '--interactive/--no-interactive', + "--interactive/--no-interactive", envvar=AppEnvVars.INTERACTIVE, default=None, help=( - 'Whether or not to allow features like prompts and progress bars (default is auto-detection) ' - '[env var: `HATCH_INTERACTIVE`]' + "Whether or not to allow features like prompts and progress bars (default is auto-detection) " + "[env var: `HATCH_INTERACTIVE`]" ), ) @click.option( - '--data-dir', + "--data-dir", envvar=ConfigEnvVars.DATA, - help='The path to a custom directory used to persist data [env var: `HATCH_DATA_DIR`]', + help="The path to a custom directory used to persist data [env var: `HATCH_DATA_DIR`]", ) @click.option( - '--cache-dir', + "--cache-dir", envvar=ConfigEnvVars.CACHE, - help='The path to a custom directory used to cache data [env var: `HATCH_CACHE_DIR`]', + help="The path to a custom directory used to cache data [env var: `HATCH_CACHE_DIR`]", ) @click.option( - '--config', - 'config_file', + "--config", + "config_file", envvar=ConfigEnvVars.CONFIG, - help='The path to a custom config file to use [env var: `HATCH_CONFIG`]', + help="The path to a custom config file to use [env var: `HATCH_CONFIG`]", ) -@click.version_option(version=__version__, prog_name='Hatch') +@click.version_option(version=__version__, prog_name="Hatch") @click.pass_context def hatch(ctx: click.Context, env_name, project, verbose, quiet, color, interactive, data_dir, cache_dir, config_file): """ @@ -103,9 +103,9 @@ def hatch(ctx: click.Context, env_name, project, verbose, quiet, color, interact \\_| |_/\\__,_|\\__\\___|_| |_| """ if color is None: - if os.environ.get(AppEnvVars.NO_COLOR) == '1': + if os.environ.get(AppEnvVars.NO_COLOR) == "1": color = False - elif os.environ.get(AppEnvVars.FORCE_COLOR) == '1': + elif os.environ.get(AppEnvVars.FORCE_COLOR) == "1": color = True if interactive is None and running_in_ci(): @@ -116,8 +116,8 @@ def hatch(ctx: click.Context, env_name, project, verbose, quiet, color, interact app.env_active = os.environ.get(AppEnvVars.ENV_ACTIVE) if ( app.env_active - and (param_source := ctx.get_parameter_source('env_name')) is not None - and param_source.name == 'DEFAULT' + and (param_source := ctx.get_parameter_source("env_name")) is not None + and param_source.name == "DEFAULT" ): app.env = app.env_active else: @@ -126,18 +126,18 @@ def hatch(ctx: click.Context, env_name, project, verbose, quiet, color, interact if config_file: app.config_file.path = Path(config_file).resolve() if not app.config_file.path.is_file(): - app.abort(f'The selected config file `{app.config_file.path}` does not exist.') + app.abort(f"The selected config file `{app.config_file.path}` does not exist.") elif not app.config_file.path.is_file(): if app.verbose: - app.display_waiting('No config file found, creating one with default settings now...') + app.display_waiting("No config file found, creating one with default settings now...") try: app.config_file.restore() if app.verbose: - app.display_success('Success! Please see `hatch config`.') + app.display_success("Success! Please see `hatch config`.") except OSError: # no cov app.abort( - f'Unable to create config file located at `{app.config_file.path}`. Please check your permissions.' + f"Unable to create config file located at `{app.config_file.path}`. Please check your permissions." ) if not ctx.invoked_subcommand: @@ -150,7 +150,7 @@ def hatch(ctx: click.Context, env_name, project, verbose, quiet, color, interact try: app.config_file.load() except OSError as e: # no cov - app.abort(f'Error loading configuration: {e}') + app.abort(f"Error loading configuration: {e}") app.config.terminal.styles.parse_fields() errors = app.initialize_styles(app.config.terminal.styles.raw_data) @@ -164,7 +164,7 @@ def hatch(ctx: click.Context, env_name, project, verbose, quiet, color, interact if project: potential_project = Project.from_config(app.config, project) if potential_project is None or potential_project.root is None: - app.abort(f'Unable to locate project {project}') + app.abort(f"Unable to locate project {project}") app.project = cast(Project, potential_project) app.project.set_app(app) @@ -173,32 +173,32 @@ def hatch(ctx: click.Context, env_name, project, verbose, quiet, color, interact app.project = Project(Path.cwd()) app.project.set_app(app) - if app.config.mode == 'local': + if app.config.mode == "local": return # The following logic is mostly duplicated for each branch so coverage can be asserted - if app.config.mode == 'project': + if app.config.mode == "project": if not app.config.project: - app.display_warning('Mode is set to `project` but no project is set, defaulting to the current directory') + app.display_warning("Mode is set to `project` but no project is set, defaulting to the current directory") return possible_project = Project.from_config(app.config, app.config.project) if possible_project is None: - app.display_warning(f'Unable to locate project {app.config.project}, defaulting to the current directory') + app.display_warning(f"Unable to locate project {app.config.project}, defaulting to the current directory") else: app.project = possible_project app.project.set_app(app) return - if app.config.mode == 'aware' and app.project.root is None: + if app.config.mode == "aware" and app.project.root is None: if not app.config.project: - app.display_warning('Mode is set to `aware` but no project is set, defaulting to the current directory') + app.display_warning("Mode is set to `aware` but no project is set, defaulting to the current directory") return possible_project = Project.from_config(app.config, app.config.project) if possible_project is None: - app.display_warning(f'Unable to locate project {app.config.project}, defaulting to the current directory') + app.display_warning(f"Unable to locate project {app.config.project}, defaulting to the current directory") else: app.project = possible_project @@ -226,13 +226,13 @@ def hatch(ctx: click.Context, env_name, project, verbose, quiet, color, interact def main(): # no cov try: - hatch(prog_name='hatch', windows_expand_args=False) + hatch(prog_name="hatch", windows_expand_args=False) except Exception: # noqa: BLE001 import sys from rich.console import Console console = Console() - hatch_debug = os.getenv('HATCH_DEBUG') in {'1', 'true'} + hatch_debug = os.getenv("HATCH_DEBUG") in {"1", "true"} console.print_exception(suppress=[click], show_locals=hatch_debug) sys.exit(1) diff --git a/src/hatch/cli/application.py b/src/hatch/cli/application.py index 19d1e48ee..c2bf39771 100644 --- a/src/hatch/cli/application.py +++ b/src/hatch/cli/application.py @@ -62,11 +62,11 @@ def run_shell_commands(self, context: ExecutionContext) -> None: should_display_command = not context.hide_commands and (self.verbose or len(resolved_commands) > 1) for i, raw_command in enumerate(resolved_commands, 1): if should_display_command: - self.display(f'{context.source} [{i}] | {raw_command}') + self.display(f"{context.source} [{i}] | {raw_command}") command = raw_command continue_on_error = context.force_continue - if raw_command.startswith('- '): + if raw_command.startswith("- "): continue_on_error = True command = command[2:] @@ -79,7 +79,7 @@ def run_shell_commands(self, context: ExecutionContext) -> None: continue if context.show_code_on_error: - self.abort(f'Failed with exit code: {process.returncode}', code=process.returncode) + self.abort(f"Failed with exit code: {process.returncode}", code=process.returncode) else: self.abort(code=process.returncode) @@ -109,7 +109,7 @@ def runner_context( incompatible[environment.name] = str(e) continue - self.abort(f'Environment `{env_name}` is incompatible: {e}') + self.abort(f"Environment `{env_name}` is incompatible: {e}") any_compatible = True if display_header: @@ -123,12 +123,12 @@ def runner_context( if incompatible: num_incompatible = len(incompatible) - padding = '\n' if any_compatible else '' + padding = "\n" if any_compatible else "" self.display_warning( - f'{padding}Skipped {num_incompatible} incompatible environment{"s" if num_incompatible > 1 else ""}:' + f"{padding}Skipped {num_incompatible} incompatible environment{'s' if num_incompatible > 1 else ''}:" ) for env_name, reason in incompatible.items(): - self.display_warning(f'{env_name} -> {reason}') + self.display_warning(f"{env_name} -> {reason}") def execute_context(self, context: ExecutionContext) -> None: from hatch.utils.structures import EnvVars @@ -138,7 +138,7 @@ def execute_context(self, context: ExecutionContext) -> None: def ensure_environment_plugin_dependencies(self) -> None: self.ensure_plugin_dependencies( - self.project.config.env_requires_complex, wait_message='Syncing environment plugin requirements' + self.project.config.env_requires_complex, wait_message="Syncing environment plugin requirements" ) def ensure_plugin_dependencies(self, dependencies: list[Requirement], *, wait_message: str) -> None: @@ -148,23 +148,23 @@ def ensure_plugin_dependencies(self, dependencies: list[Requirement], *, wait_me from hatch.dep.sync import dependencies_in_sync from hatch.env.utils import add_verbosity_flag - if app_path := os.environ.get('PYAPP'): + if app_path := os.environ.get("PYAPP"): from hatch.utils.env import PythonInfo - management_command = os.environ['PYAPP_COMMAND_NAME'] - executable = self.platform.check_command_output([app_path, management_command, 'python-path']).strip() + management_command = os.environ["PYAPP_COMMAND_NAME"] + executable = self.platform.check_command_output([app_path, management_command, "python-path"]).strip() python_info = PythonInfo(self.platform, executable=executable) if dependencies_in_sync(dependencies, sys_path=python_info.sys_path): return - pip_command = [app_path, management_command, 'pip'] + pip_command = [app_path, management_command, "pip"] else: if dependencies_in_sync(dependencies): return - pip_command = [sys.executable, '-u', '-m', 'pip'] + pip_command = [sys.executable, "-u", "-m", "pip"] - pip_command.extend(['install', '--disable-pip-version-check']) + pip_command.extend(["install", "--disable-pip-version-check"]) # Default to -1 verbosity add_verbosity_flag(pip_command, self.verbosity, adjustment=-1) @@ -184,14 +184,14 @@ def get_env_directory(self, environment_type): return self.project.location / path - return self.data_dir / 'env' / environment_type + return self.data_dir / "env" / environment_type def get_python_manager(self, directory: str | None = None): from hatch.python.core import PythonManager configured_dir = directory or self.config.dirs.python - if configured_dir == 'isolated': - return PythonManager(self.data_dir / 'pythons') + if configured_dir == "isolated": + return PythonManager(self.data_dir / "pythons") return PythonManager(Path(configured_dir).expand()) @@ -201,7 +201,7 @@ def shell_data(self) -> tuple[str, str]: return detect_shell(self.platform) - def abort(self, text='', code=1, **kwargs): + def abort(self, text="", code=1, **kwargs): if text: self.display_error(text, **kwargs) self.__exit_func(code) diff --git a/src/hatch/cli/build/__init__.py b/src/hatch/cli/build/__init__.py index 7b5c63f69..66dd9babb 100644 --- a/src/hatch/cli/build/__init__.py +++ b/src/hatch/cli/build/__init__.py @@ -8,47 +8,46 @@ from hatch.cli.application import Application -@click.command(short_help='Build a project') -@click.argument('location', required=False) +@click.command(short_help="Build a project") +@click.argument("location", required=False) @click.option( - '--target', - '-t', - 'targets', + "--target", + "-t", + "targets", multiple=True, help=( - 'The target to build, overriding project defaults. ' - 'This may be selected multiple times e.g. `-t sdist -t wheel`' + "The target to build, overriding project defaults. This may be selected multiple times e.g. `-t sdist -t wheel`" ), ) @click.option( - '--hooks-only', is_flag=True, help='Whether or not to only execute build hooks [env var: `HATCH_BUILD_HOOKS_ONLY`]' + "--hooks-only", is_flag=True, help="Whether or not to only execute build hooks [env var: `HATCH_BUILD_HOOKS_ONLY`]" ) @click.option( - '--no-hooks', is_flag=True, help='Whether or not to disable build hooks [env var: `HATCH_BUILD_NO_HOOKS`]' + "--no-hooks", is_flag=True, help="Whether or not to disable build hooks [env var: `HATCH_BUILD_NO_HOOKS`]" ) @click.option( - '--ext', + "--ext", is_flag=True, help=( - 'Whether or not to only execute build hooks for distributing binary Python packages, such as ' - 'compiling extensions. Equivalent to `--hooks-only -t wheel`' + "Whether or not to only execute build hooks for distributing binary Python packages, such as " + "compiling extensions. Equivalent to `--hooks-only -t wheel`" ), ) @click.option( - '--clean', - '-c', + "--clean", + "-c", is_flag=True, - help='Whether or not existing artifacts should first be removed [env var: `HATCH_BUILD_CLEAN`]', + help="Whether or not existing artifacts should first be removed [env var: `HATCH_BUILD_CLEAN`]", ) @click.option( - '--clean-hooks-after', + "--clean-hooks-after", is_flag=True, help=( - 'Whether or not build hook artifacts should be removed after each build ' - '[env var: `HATCH_BUILD_CLEAN_HOOKS_AFTER`]' + "Whether or not build hook artifacts should be removed after each build " + "[env var: `HATCH_BUILD_CLEAN_HOOKS_AFTER`]" ), ) -@click.option('--clean-only', is_flag=True, hidden=True) +@click.option("--clean-only", is_flag=True, hidden=True) @click.pass_obj def build(app: Application, location, targets, hooks_only, no_hooks, ext, clean, clean_hooks_after, clean_only): """Build a project.""" @@ -64,9 +63,9 @@ def build(app: Application, location, targets, hooks_only, no_hooks, ext, clean, build_dir = Path(location).resolve() if location else None if ext: hooks_only = True - targets = ('wheel',) + targets = ("wheel",) elif not targets: - targets = ('sdist', 'wheel') + targets = ("sdist", "wheel") env_vars = {} if app.verbose: @@ -75,26 +74,26 @@ def build(app: Application, location, targets, hooks_only, no_hooks, ext, clean, env_vars[AppEnvVars.QUIET] = str(abs(app.verbosity)) with EnvVars(env_vars): - app.project.prepare_build_environment(targets=[target.split(':')[0] for target in targets]) + app.project.prepare_build_environment(targets=[target.split(":")[0] for target in targets]) build_backend = app.project.metadata.build.build_backend with app.project.location.as_cwd(), app.project.build_env.get_env_vars(): for target in targets: - target_name, _, _ = target.partition(':') + target_name, _, _ = target.partition(":") if not clean_only: app.display_header(target_name) if build_backend != BUILD_BACKEND: - if target_name == 'sdist': + if target_name == "sdist": directory = build_dir or app.project.location / DEFAULT_BUILD_DIRECTORY directory.ensure_dir_exists() artifact_path = app.project.build_frontend.build_sdist(directory) - elif target_name == 'wheel': + elif target_name == "wheel": directory = build_dir or app.project.location / DEFAULT_BUILD_DIRECTORY directory.ensure_dir_exists() artifact_path = app.project.build_frontend.build_wheel(directory) else: - app.abort(f'Target `{target_name}` is not supported by `{build_backend}`') + app.abort(f"Target `{target_name}` is not supported by `{build_backend}`") app.display_info( str(artifact_path.relative_to(app.project.location)) @@ -102,27 +101,27 @@ def build(app: Application, location, targets, hooks_only, no_hooks, ext, clean, else str(artifact_path) ) else: - command = ['python', '-u', '-m', 'hatchling', 'build', '--target', target] + command = ["python", "-u", "-m", "hatchling", "build", "--target", target] # We deliberately pass the location unchanged so that absolute paths may be non-local # and reflect wherever builds actually take place if location: - command.extend(('--directory', location)) + command.extend(("--directory", location)) if hooks_only or env_var_enabled(BuildEnvVars.HOOKS_ONLY): - command.append('--hooks-only') + command.append("--hooks-only") if no_hooks or env_var_enabled(BuildEnvVars.NO_HOOKS): - command.append('--no-hooks') + command.append("--no-hooks") if clean or env_var_enabled(BuildEnvVars.CLEAN): - command.append('--clean') + command.append("--clean") if clean_hooks_after or env_var_enabled(BuildEnvVars.CLEAN_HOOKS_AFTER): - command.append('--clean-hooks-after') + command.append("--clean-hooks-after") if clean_only: - command.append('--clean-only') + command.append("--clean-only") context = ExecutionContext(app.project.build_env) context.add_shell_command(command) diff --git a/src/hatch/cli/clean/__init__.py b/src/hatch/cli/clean/__init__.py index f2363f541..3eb0ddd91 100644 --- a/src/hatch/cli/clean/__init__.py +++ b/src/hatch/cli/clean/__init__.py @@ -1,34 +1,34 @@ import click -@click.command(short_help='Remove build artifacts') -@click.argument('location', required=False) +@click.command(short_help="Remove build artifacts") +@click.argument("location", required=False) @click.option( - '--target', - '-t', - 'targets', + "--target", + "-t", + "targets", multiple=True, help=( - 'The target with which to remove artifacts, overriding project defaults. ' - 'This may be selected multiple times e.g. `-t sdist -t wheel`' + "The target with which to remove artifacts, overriding project defaults. " + "This may be selected multiple times e.g. `-t sdist -t wheel`" ), ) @click.option( - '--hooks-only', + "--hooks-only", is_flag=True, - help='Whether or not to only remove artifacts from build hooks [env var: `HATCH_BUILD_HOOKS_ONLY`]', + help="Whether or not to only remove artifacts from build hooks [env var: `HATCH_BUILD_HOOKS_ONLY`]", ) @click.option( - '--no-hooks', + "--no-hooks", is_flag=True, - help='Whether or not to ignore artifacts from build hooks [env var: `HATCH_BUILD_NO_HOOKS`]', + help="Whether or not to ignore artifacts from build hooks [env var: `HATCH_BUILD_NO_HOOKS`]", ) @click.option( - '--ext', + "--ext", is_flag=True, help=( - 'Whether or not to only remove artifacts from build hooks for distributing binary Python packages, such as ' - 'compiled extensions. Equivalent to `--hooks-only -t wheel`' + "Whether or not to only remove artifacts from build hooks for distributing binary Python packages, such as " + "compiled extensions. Equivalent to `--hooks-only -t wheel`" ), ) @click.pass_context diff --git a/src/hatch/cli/config/__init__.py b/src/hatch/cli/config/__init__.py index f5eec260a..8daf65e2c 100644 --- a/src/hatch/cli/config/__init__.py +++ b/src/hatch/cli/config/__init__.py @@ -3,56 +3,56 @@ import click -@click.group(short_help='Manage the config file') +@click.group(short_help="Manage the config file") def config(): pass -@config.command(short_help='Open the config location in your file manager') +@config.command(short_help="Open the config location in your file manager") @click.pass_obj def explore(app): """Open the config location in your file manager.""" click.launch(str(app.config_file.path), locate=True) -@config.command(short_help='Show the location of the config file') +@config.command(short_help="Show the location of the config file") @click.pass_obj def find(app): """Show the location of the config file.""" app.display(str(app.config_file.path)) -@config.command(short_help='Show the contents of the config file') -@click.option('--all', '-a', 'all_keys', is_flag=True, help='Do not scrub secret fields') +@config.command(short_help="Show the contents of the config file") +@click.option("--all", "-a", "all_keys", is_flag=True, help="Do not scrub secret fields") @click.pass_obj def show(app, all_keys): """Show the contents of the config file.""" if not app.config_file.path.is_file(): # no cov - app.display_critical('No config file found! Please try `hatch config restore`.') + app.display_critical("No config file found! Please try `hatch config restore`.") else: text = app.config_file.read() if all_keys else app.config_file.read_scrubbed() - app.display_syntax(text.rstrip(), 'toml') + app.display_syntax(text.rstrip(), "toml") -@config.command(short_help='Update the config file with any new fields') +@config.command(short_help="Update the config file with any new fields") @click.pass_obj def update(app): # no cov """Update the config file with any new fields.""" app.config_file.update() - app.display_success('Settings were successfully updated.') + app.display_success("Settings were successfully updated.") -@config.command(short_help='Restore the config file to default settings') +@config.command(short_help="Restore the config file to default settings") @click.pass_obj def restore(app): """Restore the config file to default settings.""" app.config_file.restore() - app.display_success('Settings were successfully restored.') + app.display_success("Settings were successfully restored.") -@config.command('set', short_help='Assign values to config file entries') -@click.argument('key') -@click.argument('value', required=False) +@config.command("set", short_help="Assign values to config file entries") +@click.argument("key") +@click.argument("value", required=False) @click.pass_obj def set_value(app, key, value): """ @@ -66,18 +66,18 @@ def set_value(app, key, value): from hatch.config.model import ConfigurationError, RootConfig from hatch.config.utils import create_toml_document, save_toml_document - scrubbing = key.startswith('publish.') + scrubbing = key.startswith("publish.") if value is None: - value = click.prompt(f'Value for `{key}`', hide_input=scrubbing) + value = click.prompt(f"Value for `{key}`", hide_input=scrubbing) - setting_project_location = bool(fnmatch(key, 'projects.*') or fnmatch(key, 'projects.*.location')) - if setting_project_location and not value.startswith('~'): + setting_project_location = bool(fnmatch(key, "projects.*") or fnmatch(key, "projects.*.location")) + if setting_project_location and not value.startswith("~"): value = os.path.abspath(value) user_config = new_config = tomlkit.parse(app.config_file.read()) data = [value] - data.extend(reversed(key.split('.'))) + data.extend(reversed(key.split("."))) key = data.pop() value = data.pop() @@ -86,12 +86,12 @@ def set_value(app, key, value): # Consider dots as keys while data: - default_branch = {value: ''} + default_branch = {value: ""} branch_config[key] = default_branch branch_config = branch_config[key] new_value = new_config.get(key) - if not hasattr(new_value, 'get'): + if not hasattr(new_value, "get"): new_value = default_branch new_config[key] = new_value @@ -100,26 +100,26 @@ def set_value(app, key, value): key = value value = data.pop() - if value.startswith(('{', '[')): + if value.startswith(("{", "[")): from ast import literal_eval value = literal_eval(value) - elif value.lower() == 'true': + elif value.lower() == "true": value = True - elif value.lower() == 'false': + elif value.lower() == "false": value = False branch_config[key] = new_config[key] = value # https://github.com/sdispater/tomlkit/issues/48 - if new_config.__class__.__name__ == 'Table': # no cov - table_body = getattr(new_config.value, 'body', []) + if new_config.__class__.__name__ == "Table": # no cov + table_body = getattr(new_config.value, "body", []) possible_whitespace = table_body[-2:] if len(possible_whitespace) == 2: # noqa: PLR2004 for key, item in possible_whitespace: if key is not None: break - if item.__class__.__name__ != 'Whitespace': + if item.__class__.__name__ != "Whitespace": break else: del table_body[-2] @@ -130,20 +130,20 @@ def set_value(app, key, value): app.display_error(str(e)) app.abort() else: - if not user_config['project'] and setting_project_location: - project = next(iter(branch_config_root['projects'])) - user_config['project'] = project - branch_config_root['project'] = project + if not user_config["project"] and setting_project_location: + project = next(iter(branch_config_root["projects"])) + user_config["project"] = project + branch_config_root["project"] = project save_toml_document(user_config, app.config_file.path) document = create_toml_document(branch_config_root) - if scrubbing and 'publish' in document: - for data in document['publish'].values(): + if scrubbing and "publish" in document: + for data in document["publish"].values(): for field in list(data): - data[field] = '<...>' + data[field] = "<...>" from rich.syntax import Syntax - app.display_success('New setting:') - app.output(Syntax(tomlkit.dumps(document).rstrip(), 'toml', background_color='default')) + app.display_success("New setting:") + app.output(Syntax(tomlkit.dumps(document).rstrip(), "toml", background_color="default")) diff --git a/src/hatch/cli/dep/__init__.py b/src/hatch/cli/dep/__init__.py index 4202cb002..fa054acb7 100644 --- a/src/hatch/cli/dep/__init__.py +++ b/src/hatch/cli/dep/__init__.py @@ -1,14 +1,14 @@ import click -@click.group(short_help='Manage environment dependencies') +@click.group(short_help="Manage environment dependencies") def dep(): pass -@dep.command('hash', short_help='Output a hash of the currently defined dependencies') -@click.option('--project-only', '-p', is_flag=True, help='Whether or not to exclude environment dependencies') -@click.option('--env-only', '-e', is_flag=True, help='Whether or not to exclude project dependencies') +@dep.command("hash", short_help="Output a hash of the currently defined dependencies") +@click.option("--project-only", "-p", is_flag=True, help="Whether or not to exclude environment dependencies") +@click.option("--env-only", "-e", is_flag=True, help="Whether or not to exclude project dependencies") @click.pass_obj def hash_dependencies(app, project_only, env_only): """Output a hash of the currently defined dependencies.""" @@ -34,16 +34,16 @@ def hash_dependencies(app, project_only, env_only): app.display(hash_dependencies(all_requirements)) -@dep.group(short_help='Display dependencies in various formats') +@dep.group(short_help="Display dependencies in various formats") def show(): pass -@show.command(short_help='Enumerate dependencies in a tabular format') -@click.option('--project-only', '-p', is_flag=True, help='Whether or not to exclude environment dependencies') -@click.option('--env-only', '-e', is_flag=True, help='Whether or not to exclude project dependencies') -@click.option('--lines', '-l', 'show_lines', is_flag=True, help='Whether or not to show lines between table rows') -@click.option('--ascii', 'force_ascii', is_flag=True, help='Whether or not to only use ASCII characters') +@show.command(short_help="Enumerate dependencies in a tabular format") +@click.option("--project-only", "-p", is_flag=True, help="Whether or not to exclude environment dependencies") +@click.option("--env-only", "-e", is_flag=True, help="Whether or not to exclude project dependencies") +@click.option("--lines", "-l", "show_lines", is_flag=True, help="Whether or not to show lines between table rows") +@click.option("--ascii", "force_ascii", is_flag=True, help="Whether or not to only use ASCII characters") @click.pass_obj def table(app, project_only, env_only, show_lines, force_ascii): """Enumerate dependencies in a tabular format.""" @@ -70,51 +70,51 @@ def table(app, project_only, env_only, show_lines, force_ascii): environment_requirements.extend(environment.environment_dependencies_complex) for all_requirements, table_title in ( - (project_requirements, 'Project'), - (environment_requirements, f'Env: {app.env}'), + (project_requirements, "Project"), + (environment_requirements, f"Env: {app.env}"), ): if not all_requirements: continue normalized_requirements = [Requirement(d) for d in get_normalized_dependencies(all_requirements)] - columns = {'Name': {}, 'URL': {}, 'Versions': {}, 'Markers': {}, 'Features': {}} + columns = {"Name": {}, "URL": {}, "Versions": {}, "Markers": {}, "Features": {}} for i, requirement in enumerate(normalized_requirements): - columns['Name'][i] = requirement.name + columns["Name"][i] = requirement.name if requirement.url: - columns['URL'][i] = str(requirement.url) + columns["URL"][i] = str(requirement.url) if requirement.specifier: - columns['Versions'][i] = str(requirement.specifier) + columns["Versions"][i] = str(requirement.specifier) if requirement.marker: - columns['Markers'][i] = normalize_marker_quoting(str(requirement.marker)) + columns["Markers"][i] = normalize_marker_quoting(str(requirement.marker)) if requirement.extras: - columns['Features'][i] = ', '.join(sorted(requirement.extras)) + columns["Features"][i] = ", ".join(sorted(requirement.extras)) column_options = {} for column_title in columns: - if column_title != 'URL': - column_options[column_title] = {'no_wrap': True} + if column_title != "URL": + column_options[column_title] = {"no_wrap": True} app.display_table( table_title, columns, show_lines=show_lines, column_options=column_options, force_ascii=force_ascii ) -@show.command(short_help='Enumerate dependencies as a list of requirements') -@click.option('--project-only', '-p', is_flag=True, help='Whether or not to exclude environment dependencies') -@click.option('--env-only', '-e', is_flag=True, help='Whether or not to exclude project dependencies') +@show.command(short_help="Enumerate dependencies as a list of requirements") +@click.option("--project-only", "-p", is_flag=True, help="Whether or not to exclude environment dependencies") +@click.option("--env-only", "-e", is_flag=True, help="Whether or not to exclude project dependencies") @click.option( - '--feature', - '-f', - 'features', + "--feature", + "-f", + "features", multiple=True, - help='Whether or not to only show the dependencies of the specified features', + help="Whether or not to only show the dependencies of the specified features", ) -@click.option('--all', 'all_features', is_flag=True, help='Whether or not to include the dependencies of all features') +@click.option("--all", "all_features", is_flag=True, help="Whether or not to include the dependencies of all features") @click.pass_obj def requirements(app, project_only, env_only, features, all_features): """Enumerate dependencies as a list of requirements.""" @@ -133,7 +133,7 @@ def requirements(app, project_only, env_only, features, all_features): for raw_feature in features: feature = normalize_project_name(raw_feature) if feature not in optional_dependencies_complex: - app.abort(f'Feature `{feature}` is not defined in field `project.optional-dependencies`') + app.abort(f"Feature `{feature}` is not defined in field `project.optional-dependencies`") all_requirements.extend(optional_dependencies_complex[feature].values()) elif project_only: diff --git a/src/hatch/cli/env/__init__.py b/src/hatch/cli/env/__init__.py index 422c7f2f3..901deeb0e 100644 --- a/src/hatch/cli/env/__init__.py +++ b/src/hatch/cli/env/__init__.py @@ -8,7 +8,7 @@ from hatch.cli.env.show import show -@click.group(short_help='Manage project environments') +@click.group(short_help="Manage project environments") def env(): pass diff --git a/src/hatch/cli/env/create.py b/src/hatch/cli/env/create.py index 2247c19a9..928ff0ec1 100644 --- a/src/hatch/cli/env/create.py +++ b/src/hatch/cli/env/create.py @@ -8,8 +8,8 @@ from hatch.cli.application import Application -@click.command(short_help='Create environments') -@click.argument('env_name', default='default') +@click.command(short_help="Create environments") +@click.argument("env_name", default="default") @click.pass_obj def create(app: Application, env_name: str): """Create environments.""" @@ -17,13 +17,13 @@ def create(app: Application, env_name: str): environments = app.project.expand_environments(env_name) if not environments: - app.abort(f'Environment `{env_name}` is not defined by project config') + app.abort(f"Environment `{env_name}` is not defined by project config") incompatible = {} for env in environments: environment = app.project.get_environment(env) if environment.exists(): - app.display_warning(f'Environment `{env}` already exists') + app.display_warning(f"Environment `{env}` already exists") continue try: @@ -33,14 +33,14 @@ def create(app: Application, env_name: str): incompatible[env] = str(e) continue - app.abort(f'Environment `{env}` is incompatible: {e}') + app.abort(f"Environment `{env}` is incompatible: {e}") app.project.prepare_environment(environment) if incompatible: num_incompatible = len(incompatible) app.display_warning( - f'Skipped {num_incompatible} incompatible environment{"s" if num_incompatible > 1 else ""}:' + f"Skipped {num_incompatible} incompatible environment{'s' if num_incompatible > 1 else ''}:" ) for env, reason in incompatible.items(): - app.display_warning(f'{env} -> {reason}') + app.display_warning(f"{env} -> {reason}") diff --git a/src/hatch/cli/env/find.py b/src/hatch/cli/env/find.py index 7dffdf30c..7662706a7 100644 --- a/src/hatch/cli/env/find.py +++ b/src/hatch/cli/env/find.py @@ -8,8 +8,8 @@ from hatch.cli.application import Application -@click.command(short_help='Locate environments') -@click.argument('env_name', default='default') +@click.command(short_help="Locate environments") +@click.argument("env_name", default="default") @click.pass_obj def find(app: Application, env_name: str): """Locate environments.""" @@ -17,7 +17,7 @@ def find(app: Application, env_name: str): environments = app.project.expand_environments(env_name) if not environments: - app.abort(f'Environment `{env_name}` is not defined by project config') + app.abort(f"Environment `{env_name}` is not defined by project config") for env in environments: environment = app.project.get_environment(env) diff --git a/src/hatch/cli/env/prune.py b/src/hatch/cli/env/prune.py index 8d4db5795..4328cfbcc 100644 --- a/src/hatch/cli/env/prune.py +++ b/src/hatch/cli/env/prune.py @@ -8,7 +8,7 @@ from hatch.cli.application import Application -@click.command(short_help='Remove all environments') +@click.command(short_help="Remove all environments") @click.pass_obj def prune(app: Application): """Remove all environments.""" @@ -19,12 +19,12 @@ def prune(app: Application): for environments in (app.project.config.envs, app.project.config.internal_envs): for env_name in environments: if env_name == app.env_active: - app.abort(f'Cannot remove active environment: {env_name}') + app.abort(f"Cannot remove active environment: {env_name}") for env_name, config in environments.items(): - environment_type = config['type'] + environment_type = config["type"] if environment_type not in environment_types: - app.abort(f'Environment `{env_name}` has unknown type: {environment_type}') + app.abort(f"Environment `{env_name}` has unknown type: {environment_type}") environment = environment_types[environment_type]( app.project.location, @@ -33,11 +33,11 @@ def prune(app: Application): config, app.project.config.matrix_variables.get(env_name, {}), app.get_env_directory(environment_type), - app.data_dir / 'env' / environment_type, + app.data_dir / "env" / environment_type, app.platform, app.verbosity, app, ) if environment.exists(): - with app.status(f'Removing environment: {env_name}'): + with app.status(f"Removing environment: {env_name}"): environment.remove() diff --git a/src/hatch/cli/env/remove.py b/src/hatch/cli/env/remove.py index 0e99d7acc..0a978a59d 100644 --- a/src/hatch/cli/env/remove.py +++ b/src/hatch/cli/env/remove.py @@ -8,27 +8,27 @@ from hatch.cli.application import Application -@click.command(short_help='Remove environments') -@click.argument('env_name', default='default') +@click.command(short_help="Remove environments") +@click.argument("env_name", default="default") @click.pass_context def remove(ctx: click.Context, env_name: str): """Remove environments.""" app: Application = ctx.obj app.ensure_environment_plugin_dependencies() - if (parameter_source := ctx.get_parameter_source('env_name')) is not None and parameter_source.name == 'DEFAULT': + if (parameter_source := ctx.get_parameter_source("env_name")) is not None and parameter_source.name == "DEFAULT": env_name = app.env environments = app.project.expand_environments(env_name) if not environments: - app.abort(f'Environment `{env_name}` is not defined by project config') + app.abort(f"Environment `{env_name}` is not defined by project config") for env_name in environments: if env_name == app.env_active: - app.abort(f'Cannot remove active environment: {env_name}') + app.abort(f"Cannot remove active environment: {env_name}") for env_name in environments: environment = app.project.get_environment(env_name) if environment.exists(): - with app.status(f'Removing environment: {env_name}'): + with app.status(f"Removing environment: {env_name}"): environment.remove() diff --git a/src/hatch/cli/env/run.py b/src/hatch/cli/env/run.py index 23606d866..b675ee976 100644 --- a/src/hatch/cli/env/run.py +++ b/src/hatch/cli/env/run.py @@ -20,16 +20,16 @@ def filter_environments(environments, filter_data): return selected_environments -@click.command(short_help='Run commands within project environments') -@click.argument('args', required=True, nargs=-1) -@click.option('--env', '-e', 'env_names', multiple=True, help='The environments to target') -@click.option('--include', '-i', 'included_variable_specs', multiple=True, help='The matrix variables to include') -@click.option('--exclude', '-x', 'excluded_variable_specs', multiple=True, help='The matrix variables to exclude') -@click.option('--filter', '-f', 'filter_json', default=None, help='The JSON data used to select environments') +@click.command(short_help="Run commands within project environments") +@click.argument("args", required=True, nargs=-1) +@click.option("--env", "-e", "env_names", multiple=True, help="The environments to target") +@click.option("--include", "-i", "included_variable_specs", multiple=True, help="The matrix variables to include") +@click.option("--exclude", "-x", "excluded_variable_specs", multiple=True, help="The matrix variables to exclude") +@click.option("--filter", "-f", "filter_json", default=None, help="The JSON data used to select environments") @click.option( - '--force-continue', is_flag=True, help='Run every command and if there were any errors exit with the first code' + "--force-continue", is_flag=True, help="Run every command and if there were any errors exit with the first code" ) -@click.option('--ignore-compat', is_flag=True, help='Ignore incompatibility when selecting specific environments') +@click.option("--ignore-compat", is_flag=True, help="Ignore incompatibility when selecting specific environments") @click.pass_obj def run( app: Application, @@ -79,12 +79,12 @@ def run( try: included_variables = parse_matrix_variables(included_variable_specs) except ValueError as e: - app.abort(f'Duplicate included variable: {e}') + app.abort(f"Duplicate included variable: {e}") try: excluded_variables = parse_matrix_variables(excluded_variable_specs) except ValueError as e: - app.abort(f'Duplicate excluded variable: {e}') + app.abort(f"Duplicate excluded variable: {e}") app.ensure_environment_plugin_dependencies() @@ -92,12 +92,12 @@ def run( if not env_names: env_names = (app.env,) - elif 'system' in env_names: - project.config.config['envs'] = { - 'system': { - 'type': 'system', - 'skip-install': True, - 'scripts': project.config.scripts, + elif "system" in env_names: + project.config.config["envs"] = { + "system": { + "type": "system", + "skip-install": True, + "scripts": project.config.scripts, } } @@ -109,9 +109,9 @@ def run( for env_name in ordered_env_names: if env_name in project.config.matrices: matrix_selected = True - env_data = project.config.matrices[env_name]['envs'] + env_data = project.config.matrices[env_name]["envs"] if not env_data: - app.abort(f'No variables defined for matrix: {env_name}') + app.abort(f"No variables defined for matrix: {env_name}") environments.extend(select_environments(env_data, included_variables, excluded_variables)) else: @@ -122,21 +122,21 @@ def run( filter_data = json.loads(filter_json) if not isinstance(filter_data, dict): - app.abort('The --filter/-f option must be a JSON mapping') + app.abort("The --filter/-f option must be a JSON mapping") environments[:] = filter_environments(project.config.envs, filter_data) if not environments: - app.abort('No environments were selected') + app.abort("No environments were selected") elif not matrix_selected and (included_variables or excluded_variables): - app.abort(f'Variable selection is unsupported for non-matrix environments: {", ".join(ordered_env_names)}') + app.abort(f"Variable selection is unsupported for non-matrix environments: {', '.join(ordered_env_names)}") for context in app.runner_context( environments, ignore_compat=ignore_compat or matrix_selected, display_header=matrix_selected, ): - if context.env.name == 'system': + if context.env.name == "system": context.env.exists = lambda: True # type: ignore[method-assign] context.force_continue = force_continue diff --git a/src/hatch/cli/env/show.py b/src/hatch/cli/env/show.py index 134368efb..e968d58aa 100644 --- a/src/hatch/cli/env/show.py +++ b/src/hatch/cli/env/show.py @@ -8,12 +8,12 @@ from hatch.cli.application import Application -@click.command(short_help='Show the available environments') -@click.argument('envs', required=False, nargs=-1) -@click.option('--ascii', 'force_ascii', is_flag=True, help='Whether or not to only use ASCII characters') -@click.option('--json', 'as_json', is_flag=True, help='Whether or not to output in JSON format') -@click.option('--internal', '-i', is_flag=True, help='Show internal environments') -@click.option('--hide-titles', is_flag=True, hidden=True) +@click.command(short_help="Show the available environments") +@click.argument("envs", required=False, nargs=-1) +@click.option("--ascii", "force_ascii", is_flag=True, help="Whether or not to only use ASCII characters") +@click.option("--json", "as_json", is_flag=True, help="Whether or not to output in JSON format") +@click.option("--internal", "-i", is_flag=True, help="Show internal environments") +@click.option("--hide-titles", is_flag=True, hidden=True) @click.pass_obj def show( app: Application, @@ -41,33 +41,33 @@ def show( env_vars = dict(environment.env_vars) env_vars.pop(AppEnvVars.ENV_ACTIVE) if env_vars: - new_config['env-vars'] = env_vars + new_config["env-vars"] = env_vars - num_dependencies = len(config.get('dependencies', [])) + num_dependencies = len(config.get("dependencies", [])) dependencies = environment.environment_dependencies[:num_dependencies] if dependencies: - new_config['dependencies'] = dependencies + new_config["dependencies"] = dependencies extra_dependencies = environment.environment_dependencies[num_dependencies:] if extra_dependencies: - new_config['extra-dependencies'] = extra_dependencies + new_config["extra-dependencies"] = extra_dependencies if environment.pre_install_commands: - new_config['pre-install-commands'] = list( + new_config["pre-install-commands"] = list( environment.resolve_commands(environment.pre_install_commands) ) if environment.post_install_commands: - new_config['post-install-commands'] = list( + new_config["post-install-commands"] = list( environment.resolve_commands(environment.post_install_commands) ) if environment.scripts: - new_config['scripts'] = { + new_config["scripts"] = { script: list(environment.resolve_commands([script])) for script in environment.scripts } - app.display(json.dumps(contextual_config, separators=(',', ':'))) + app.display(json.dumps(contextual_config, separators=(",", ":"))) return from packaging.requirements import InvalidRequirement, Requirement @@ -83,45 +83,45 @@ def show( for env_name in envs: if env_name not in target_standalone_envs and env_name not in target_matrices: - app.abort(f'Environment `{env_name}` is not defined by project config') + app.abort(f"Environment `{env_name}` is not defined by project config") env_names = set(envs) matrix_columns: dict[str, dict[int, str]] = { - 'Name': {}, - 'Type': {}, - 'Envs': {}, - 'Features': {}, - 'Dependencies': {}, - 'Environment variables': {}, - 'Scripts': {}, - 'Description': {}, + "Name": {}, + "Type": {}, + "Envs": {}, + "Features": {}, + "Dependencies": {}, + "Environment variables": {}, + "Scripts": {}, + "Description": {}, } matrix_envs = set() for i, (matrix_name, matrix_data) in enumerate(target_matrices.items()): - matrix_envs.update(matrix_data['envs']) + matrix_envs.update(matrix_data["envs"]) if env_names and matrix_name not in env_names: continue - config = matrix_data['config'] - matrix_columns['Name'][i] = matrix_name - matrix_columns['Type'][i] = config['type'] - matrix_columns['Envs'][i] = '\n'.join(matrix_data['envs']) + config = matrix_data["config"] + matrix_columns["Name"][i] = matrix_name + matrix_columns["Type"][i] = config["type"] + matrix_columns["Envs"][i] = "\n".join(matrix_data["envs"]) - if config.get('features'): + if config.get("features"): if app.project.metadata.hatch.metadata.allow_ambiguous_features: - matrix_columns['Features'][i] = '\n'.join(sorted(set(config['features']))) + matrix_columns["Features"][i] = "\n".join(sorted(set(config["features"]))) else: - matrix_columns['Features'][i] = '\n'.join( - sorted({normalize_project_name(f) for f in config['features']}) + matrix_columns["Features"][i] = "\n".join( + sorted({normalize_project_name(f) for f in config["features"]}) ) dependencies = [] - if config.get('dependencies'): - dependencies.extend(config['dependencies']) - if config.get('extra-dependencies'): - dependencies.extend(config['extra-dependencies']) + if config.get("dependencies"): + dependencies.extend(config["dependencies"]) + if config.get("extra-dependencies"): + dependencies.extend(config["extra-dependencies"]) if dependencies: normalized_dependencies = set() for dependency in dependencies: @@ -132,29 +132,29 @@ def show( else: normalized_dependencies.add(get_normalized_dependency(req)) - matrix_columns['Dependencies'][i] = '\n'.join(sorted(normalized_dependencies)) + matrix_columns["Dependencies"][i] = "\n".join(sorted(normalized_dependencies)) - if config.get('env-vars'): - matrix_columns['Environment variables'][i] = '\n'.join( - '='.join(item) for item in sorted(config['env-vars'].items()) + if config.get("env-vars"): + matrix_columns["Environment variables"][i] = "\n".join( + "=".join(item) for item in sorted(config["env-vars"].items()) ) - if config.get('scripts'): - matrix_columns['Scripts'][i] = '\n'.join( - sorted(script for script in config['scripts'] if app.verbose or not script.startswith('_')) + if config.get("scripts"): + matrix_columns["Scripts"][i] = "\n".join( + sorted(script for script in config["scripts"] if app.verbose or not script.startswith("_")) ) - if config.get('description'): - matrix_columns['Description'][i] = config['description'].strip() + if config.get("description"): + matrix_columns["Description"][i] = config["description"].strip() standalone_columns: dict[str, dict[int, str]] = { - 'Name': {}, - 'Type': {}, - 'Features': {}, - 'Dependencies': {}, - 'Environment variables': {}, - 'Scripts': {}, - 'Description': {}, + "Name": {}, + "Type": {}, + "Features": {}, + "Dependencies": {}, + "Environment variables": {}, + "Scripts": {}, + "Description": {}, } standalone_envs = ( (env_name, config) @@ -167,46 +167,46 @@ def show( environment = app.project.get_environment(env_name) - standalone_columns['Name'][i] = env_name - standalone_columns['Type'][i] = config['type'] + standalone_columns["Name"][i] = env_name + standalone_columns["Type"][i] = config["type"] if environment.features: - standalone_columns['Features'][i] = '\n'.join(environment.features) + standalone_columns["Features"][i] = "\n".join(environment.features) if environment.environment_dependencies_complex: - standalone_columns['Dependencies'][i] = '\n'.join( + standalone_columns["Dependencies"][i] = "\n".join( sorted({get_normalized_dependency(d) for d in environment.environment_dependencies_complex}) ) env_vars = dict(environment.env_vars) env_vars.pop(AppEnvVars.ENV_ACTIVE) if env_vars: - standalone_columns['Environment variables'][i] = '\n'.join( - '='.join(item) for item in sorted(env_vars.items()) + standalone_columns["Environment variables"][i] = "\n".join( + "=".join(item) for item in sorted(env_vars.items()) ) if environment.scripts: - standalone_columns['Scripts'][i] = '\n'.join( - sorted(script for script in environment.scripts if app.verbose or not script.startswith('_')) + standalone_columns["Scripts"][i] = "\n".join( + sorted(script for script in environment.scripts if app.verbose or not script.startswith("_")) ) if environment.description: - standalone_columns['Description'][i] = environment.description.strip() + standalone_columns["Description"][i] = environment.description.strip() column_options = {} for title in matrix_columns: - if title != 'Description': - column_options[title] = {'no_wrap': True} + if title != "Description": + column_options[title] = {"no_wrap": True} app.display_table( - '' if hide_titles else 'Standalone', + "" if hide_titles else "Standalone", standalone_columns, show_lines=True, column_options=column_options, force_ascii=force_ascii, ) app.display_table( - '' if hide_titles else 'Matrices', + "" if hide_titles else "Matrices", matrix_columns, show_lines=True, column_options=column_options, diff --git a/src/hatch/cli/fmt/__init__.py b/src/hatch/cli/fmt/__init__.py index bc6a5518e..4b0741e1a 100644 --- a/src/hatch/cli/fmt/__init__.py +++ b/src/hatch/cli/fmt/__init__.py @@ -8,12 +8,12 @@ from hatch.cli.application import Application -@click.command(short_help='Format and lint source code', context_settings={'ignore_unknown_options': True}) -@click.argument('args', nargs=-1) -@click.option('--check', is_flag=True, help='Only check for errors rather than fixing them') -@click.option('--linter', '-l', is_flag=True, help='Only run the linter') -@click.option('--formatter', '-f', is_flag=True, help='Only run the formatter') -@click.option('--sync', is_flag=True, help='Sync the default config file with the current version of Hatch') +@click.command(short_help="Format and lint source code", context_settings={"ignore_unknown_options": True}) +@click.argument("args", nargs=-1) +@click.option("--check", is_flag=True, help="Only check for errors rather than fixing them") +@click.option("--linter", "-l", is_flag=True, help="Only run the linter") +@click.option("--formatter", "-f", is_flag=True, help="Only run the formatter") +@click.option("--sync", is_flag=True, help="Sync the default config file with the current version of Hatch") @click.pass_obj def fmt( app: Application, @@ -26,53 +26,53 @@ def fmt( ): """Format and lint source code.""" if linter and formatter: - app.abort('Cannot specify both --linter and --formatter') + app.abort("Cannot specify both --linter and --formatter") from hatch.cli.fmt.core import StaticAnalysisEnvironment app.ensure_environment_plugin_dependencies() - for context in app.runner_context(['hatch-static-analysis']): + for context in app.runner_context(["hatch-static-analysis"]): sa_env = StaticAnalysisEnvironment(context.env) # TODO: remove in a few minor releases, this is very new but we don't want to break users on the cutting edge - if legacy_config_path := app.project.config.config.get('format', {}).get('config-path', ''): + if legacy_config_path := app.project.config.config.get("format", {}).get("config-path", ""): app.display_warning( - 'The `tool.hatch.format.config-path` option is deprecated and will be removed in a future release. ' - 'Use `tool.hatch.envs.hatch-static-analysis.config-path` instead.' + "The `tool.hatch.format.config-path` option is deprecated and will be removed in a future release. " + "Use `tool.hatch.envs.hatch-static-analysis.config-path` instead." ) sa_env.config_path = legacy_config_path if sync and not sa_env.config_path: - app.abort('The --sync flag can only be used when the `tool.hatch.format.config-path` option is defined') + app.abort("The --sync flag can only be used when the `tool.hatch.format.config-path` option is defined") scripts: list[str] = [] if not formatter: - scripts.append('lint-check' if check else 'lint-fix') + scripts.append("lint-check" if check else "lint-fix") if not linter: - scripts.append('format-check' if check else 'format-fix') + scripts.append("format-check" if check else "format-fix") default_args = sa_env.get_default_args() arguments = list(args) try: - arguments.remove('--preview') + arguments.remove("--preview") except ValueError: preview = False else: preview = True - default_args.append('--preview') + default_args.append("--preview") internal_args = context.env.join_command_args(default_args) if internal_args: # Add an extra space if required - internal_args = f' {internal_args}' + internal_args = f" {internal_args}" formatted_args = context.env.join_command_args(arguments) for script in scripts: - context.add_shell_command(f'{script} {formatted_args}') + context.add_shell_command(f"{script} {formatted_args}") - context.env_vars['HATCH_FMT_ARGS'] = internal_args + context.env_vars["HATCH_FMT_ARGS"] = internal_args if not sa_env.config_path or sync: sa_env.write_config_file(preview=preview) diff --git a/src/hatch/cli/fmt/core.py b/src/hatch/cli/fmt/core.py index de454a342..bf682ee47 100644 --- a/src/hatch/cli/fmt/core.py +++ b/src/hatch/cli/fmt/core.py @@ -14,31 +14,31 @@ def __init__(self, env: EnvironmentInterface) -> None: @cached_property def config_path(self) -> str: - return self.env.config.get('config-path', '') + return self.env.config.get("config-path", "") def get_default_args(self) -> list[str]: default_args = [] if not self.config_path: if self.internal_user_config_file is None: - default_args.extend(['--config', str(self.internal_config_file)]) + default_args.extend(["--config", str(self.internal_config_file)]) else: - default_args.extend(['--config', str(self.internal_user_config_file)]) + default_args.extend(["--config", str(self.internal_user_config_file)]) return default_args @cached_property def internal_config_file(self) -> Path: - return self.env.isolated_data_directory / '.config' / self.env.root.id / 'ruff_defaults.toml' + return self.env.isolated_data_directory / ".config" / self.env.root.id / "ruff_defaults.toml" def construct_config_file(self, *, preview: bool) -> str: lines = [ - 'line-length = 120', - '', - '[format]', - 'docstring-code-format = true', - 'docstring-code-line-length = 80', - '', - '[lint]', + "line-length = 120", + "", + "[format]", + "docstring-code-format = true", + "docstring-code-line-length = 80", + "", + "[lint]", ] # Selected rules @@ -47,40 +47,40 @@ def construct_config_file(self, *, preview: bool) -> str: rules.extend(PREVIEW_RULES) rules.sort() - lines.append('select = [') + lines.append("select = [") lines.extend(f' "{rule}",' for rule in rules) - lines.extend((']', '')) + lines.extend(("]", "")) # Ignored rules - lines.append('[lint.per-file-ignores]') + lines.append("[lint.per-file-ignores]") for glob, ignored_rules in PER_FILE_IGNORED_RULES.items(): lines.append(f'"{glob}" = [') lines.extend(f' "{ignored_rule}",' for ignored_rule in ignored_rules) - lines.append(']') + lines.append("]") # Default config lines.extend(( - '', - '[lint.flake8-tidy-imports]', + "", + "[lint.flake8-tidy-imports]", 'ban-relative-imports = "all"', - '', - '[lint.isort]', + "", + "[lint.isort]", f'known-first-party = ["{self.env.metadata.name.replace("-", "_")}"]', - '', - '[lint.flake8-pytest-style]', - 'fixture-parentheses = false', - 'mark-parentheses = false', + "", + "[lint.flake8-pytest-style]", + "fixture-parentheses = false", + "mark-parentheses = false", )) # Ensure the file ends with a newline to satisfy other linters - lines.append('') + lines.append("") - return '\n'.join(lines) + return "\n".join(lines) def write_config_file(self, *, preview: bool) -> None: config_contents = self.construct_config_file(preview=preview) if self.config_path: - (self.env.root / self.config_path).write_atomic(config_contents, 'w', encoding='utf-8') + (self.env.root / self.config_path).write_atomic(config_contents, "w", encoding="utf-8") return self.internal_config_file.parent.ensure_dir_exists() @@ -94,21 +94,21 @@ def write_config_file(self, *, preview: bool) -> None: return old_contents = self.user_config_file.read_text() - config_path = str(self.internal_config_file).replace('\\', '\\\\') - if self.user_config_file.name == 'pyproject.toml': + config_path = str(self.internal_config_file).replace("\\", "\\\\") + if self.user_config_file.name == "pyproject.toml": lines = old_contents.splitlines() try: - index = lines.index('[tool.ruff]') + index = lines.index("[tool.ruff]") except ValueError: lines.extend(( - '', - '[tool.ruff]', + "", + "[tool.ruff]", f'extend = "{config_path}"', )) else: lines.insert(index + 1, f'extend = "{config_path}"') - contents = '\n'.join(lines) + contents = "\n".join(lines) else: contents = f'extend = "{config_path}"\n{old_contents}' @@ -124,7 +124,7 @@ def internal_user_config_file(self) -> Path | None: @cached_property def user_config_file(self) -> Path | None: # https://docs.astral.sh/ruff/configuration/#config-file-discovery - for possible_config in ('.ruff.toml', 'ruff.toml', 'pyproject.toml'): + for possible_config in (".ruff.toml", "ruff.toml", "pyproject.toml"): if (config_file := (self.env.root / possible_config)).is_file(): return config_file @@ -141,721 +141,779 @@ def user_config(self) -> dict[str, Any]: @cached_property def linter_preview(self) -> bool: - return self.get_config('lint').get('preview', False) + return self.get_config("lint").get("preview", False) @cached_property def formatter_preview(self) -> bool: - return self.get_config('format').get('preview', False) + return self.get_config("format").get("preview", False) def get_config(self, section: str) -> dict[str, Any]: if self.user_config_file is None: return {} - if self.user_config_file.name == 'pyproject.toml': - return self.user_config.get('tool', {}).get('ruff', {}).get(section, {}) + if self.user_config_file.name == "pyproject.toml": + return self.user_config.get("tool", {}).get("ruff", {}).get(section, {}) return self.user_config.get(section, {}) STABLE_RULES: tuple[str, ...] = ( - 'A001', - 'A002', - 'A003', - 'ARG001', - 'ARG002', - 'ARG003', - 'ARG004', - 'ARG005', - 'ASYNC100', - 'ASYNC105', - 'ASYNC109', - 'ASYNC110', - 'ASYNC115', - 'ASYNC210', - 'ASYNC220', - 'ASYNC221', - 'ASYNC222', - 'ASYNC230', - 'ASYNC251', - 'B002', - 'B003', - 'B004', - 'B005', - 'B006', - 'B007', - 'B008', - 'B009', - 'B010', - 'B011', - 'B012', - 'B013', - 'B014', - 'B015', - 'B016', - 'B017', - 'B018', - 'B019', - 'B020', - 'B021', - 'B022', - 'B023', - 'B024', - 'B025', - 'B026', - 'B028', - 'B029', - 'B030', - 'B031', - 'B032', - 'B033', - 'B034', - 'B035', - 'B904', - 'B905', - 'BLE001', - 'C400', - 'C401', - 'C402', - 'C403', - 'C404', - 'C405', - 'C406', - 'C408', - 'C409', - 'C410', - 'C411', - 'C413', - 'C414', - 'C415', - 'C416', - 'C417', - 'C418', - 'C419', - 'COM818', - 'DTZ001', - 'DTZ002', - 'DTZ003', - 'DTZ004', - 'DTZ005', - 'DTZ006', - 'DTZ007', - 'DTZ011', - 'DTZ012', - 'E101', - 'E401', - 'E402', - 'E701', - 'E702', - 'E703', - 'E711', - 'E712', - 'E713', - 'E714', - 'E721', - 'E722', - 'E731', - 'E741', - 'E742', - 'E743', - 'E902', - 'E999', - 'EM101', - 'EM102', - 'EM103', - 'EXE001', - 'EXE002', - 'EXE003', - 'EXE004', - 'EXE005', - 'F401', - 'F402', - 'F403', - 'F404', - 'F405', - 'F406', - 'F407', - 'F501', - 'F502', - 'F503', - 'F504', - 'F505', - 'F506', - 'F507', - 'F508', - 'F509', - 'F521', - 'F522', - 'F523', - 'F524', - 'F525', - 'F541', - 'F601', - 'F602', - 'F621', - 'F622', - 'F631', - 'F632', - 'F633', - 'F634', - 'F701', - 'F702', - 'F704', - 'F706', - 'F707', - 'F722', - 'F811', - 'F821', - 'F822', - 'F823', - 'F841', - 'F842', - 'F901', - 'FA100', - 'FA102', - 'FBT001', - 'FBT002', - 'FLY002', - 'FURB105', - 'FURB129', - 'FURB136', - 'FURB161', - 'FURB163', - 'FURB167', - 'FURB168', - 'FURB169', - 'FURB177', - 'FURB181', - 'FURB187', - 'G001', - 'G002', - 'G003', - 'G004', - 'G010', - 'G101', - 'G201', - 'G202', - 'I001', - 'I002', - 'ICN001', - 'ICN002', - 'ICN003', - 'INP001', - 'INT001', - 'INT002', - 'INT003', - 'ISC003', - 'LOG001', - 'LOG002', - 'LOG007', - 'LOG009', - 'N801', - 'N802', - 'N803', - 'N804', - 'N805', - 'N806', - 'N807', - 'N811', - 'N812', - 'N813', - 'N814', - 'N815', - 'N816', - 'N817', - 'N818', - 'N999', - 'PERF101', - 'PERF102', - 'PERF401', - 'PERF402', - 'PERF403', - 'PGH005', - 'PIE790', - 'PIE794', - 'PIE796', - 'PIE800', - 'PIE804', - 'PIE807', - 'PIE808', - 'PIE810', - 'PLC0105', - 'PLC0131', - 'PLC0132', - 'PLC0205', - 'PLC0208', - 'PLC0414', - 'PLC2401', - 'PLC2403', - 'PLC3002', - 'PLE0100', - 'PLE0101', - 'PLE0115', - 'PLE0116', - 'PLE0117', - 'PLE0118', - 'PLE0237', - 'PLE0241', - 'PLE0302', - 'PLE0303', - 'PLE0305', - 'PLE0307', - 'PLE0308', - 'PLE0309', - 'PLE0604', - 'PLE0605', - 'PLE0643', - 'PLE0704', - 'PLE1132', - 'PLE1142', - 'PLE1205', - 'PLE1206', - 'PLE1300', - 'PLE1307', - 'PLE1310', - 'PLE1507', - 'PLE1519', - 'PLE1520', - 'PLE1700', - 'PLE2502', - 'PLE2510', - 'PLE2512', - 'PLE2513', - 'PLE2514', - 'PLE2515', - 'PLR0124', - 'PLR0133', - 'PLR0206', - 'PLR0402', - 'PLR1704', - 'PLR1711', - 'PLR1714', - 'PLR1722', - 'PLR1730', - 'PLR1736', - 'PLR2004', - 'PLR2044', - 'PLR5501', - 'PLW0120', - 'PLW0127', - 'PLW0128', - 'PLW0129', - 'PLW0131', - 'PLW0133', - 'PLW0211', - 'PLW0245', - 'PLW0406', - 'PLW0602', - 'PLW0603', - 'PLW0604', - 'PLW0642', - 'PLW0711', - 'PLW1501', - 'PLW1508', - 'PLW1509', - 'PLW1510', - 'PLW2101', - 'PLW2901', - 'PLW3301', - 'PT001', - 'PT002', - 'PT003', - 'PT006', - 'PT007', - 'PT008', - 'PT009', - 'PT010', - 'PT011', - 'PT012', - 'PT013', - 'PT014', - 'PT015', - 'PT016', - 'PT017', - 'PT018', - 'PT019', - 'PT020', - 'PT021', - 'PT022', - 'PT023', - 'PT024', - 'PT025', - 'PT026', - 'PT027', - 'PYI001', - 'PYI002', - 'PYI003', - 'PYI004', - 'PYI005', - 'PYI006', - 'PYI007', - 'PYI008', - 'PYI009', - 'PYI010', - 'PYI011', - 'PYI012', - 'PYI013', - 'PYI014', - 'PYI015', - 'PYI016', - 'PYI017', - 'PYI018', - 'PYI019', - 'PYI020', - 'PYI021', - 'PYI024', - 'PYI025', - 'PYI026', - 'PYI029', - 'PYI030', - 'PYI032', - 'PYI033', - 'PYI034', - 'PYI035', - 'PYI036', - 'PYI041', - 'PYI042', - 'PYI043', - 'PYI044', - 'PYI045', - 'PYI046', - 'PYI047', - 'PYI048', - 'PYI049', - 'PYI050', - 'PYI051', - 'PYI052', - 'PYI053', - 'PYI054', - 'PYI055', - 'PYI056', - 'PYI057', - 'PYI058', - 'PYI062', - 'RET503', - 'RET504', - 'RET505', - 'RET506', - 'RET507', - 'RET508', - 'RSE102', - 'RUF001', - 'RUF002', - 'RUF003', - 'RUF005', - 'RUF006', - 'RUF007', - 'RUF008', - 'RUF009', - 'RUF010', - 'RUF012', - 'RUF013', - 'RUF015', - 'RUF016', - 'RUF017', - 'RUF018', - 'RUF019', - 'RUF020', - 'RUF024', - 'RUF026', - 'RUF100', - 'RUF101', - 'S101', - 'S102', - 'S103', - 'S104', - 'S105', - 'S106', - 'S107', - 'S108', - 'S110', - 'S112', - 'S113', - 'S201', - 'S202', - 'S301', - 'S302', - 'S303', - 'S304', - 'S305', - 'S306', - 'S307', - 'S308', - 'S310', - 'S311', - 'S312', - 'S313', - 'S314', - 'S315', - 'S316', - 'S317', - 'S318', - 'S319', - 'S320', - 'S321', - 'S323', - 'S324', - 'S501', - 'S502', - 'S503', - 'S504', - 'S505', - 'S506', - 'S507', - 'S508', - 'S509', - 'S601', - 'S602', - 'S604', - 'S605', - 'S606', - 'S607', - 'S608', - 'S609', - 'S610', - 'S611', - 'S612', - 'S701', - 'S702', - 'SIM101', - 'SIM102', - 'SIM103', - 'SIM105', - 'SIM107', - 'SIM108', - 'SIM109', - 'SIM110', - 'SIM112', - 'SIM113', - 'SIM114', - 'SIM115', - 'SIM116', - 'SIM117', - 'SIM118', - 'SIM201', - 'SIM202', - 'SIM208', - 'SIM210', - 'SIM211', - 'SIM212', - 'SIM220', - 'SIM221', - 'SIM222', - 'SIM223', - 'SIM300', - 'SIM910', - 'SIM911', - 'SLF001', - 'SLOT000', - 'SLOT001', - 'SLOT002', - 'T100', - 'T201', - 'T203', - 'TCH001', - 'TCH002', - 'TCH003', - 'TCH004', - 'TCH005', - 'TCH010', - 'TD004', - 'TD005', - 'TD006', - 'TD007', - 'TID251', - 'TID252', - 'TID253', - 'TRY002', - 'TRY003', - 'TRY004', - 'TRY201', - 'TRY300', - 'TRY301', - 'TRY302', - 'TRY400', - 'TRY401', - 'UP001', - 'UP003', - 'UP004', - 'UP005', - 'UP006', - 'UP007', - 'UP008', - 'UP009', - 'UP010', - 'UP011', - 'UP012', - 'UP013', - 'UP014', - 'UP015', - 'UP017', - 'UP018', - 'UP019', - 'UP020', - 'UP021', - 'UP022', - 'UP023', - 'UP024', - 'UP025', - 'UP026', - 'UP027', - 'UP028', - 'UP029', - 'UP030', - 'UP031', - 'UP032', - 'UP033', - 'UP034', - 'UP035', - 'UP036', - 'UP037', - 'UP038', - 'UP039', - 'UP040', - 'UP041', - 'W291', - 'W292', - 'W293', - 'W505', - 'W605', - 'YTT101', - 'YTT102', - 'YTT103', - 'YTT201', - 'YTT202', - 'YTT203', - 'YTT204', - 'YTT301', - 'YTT302', - 'YTT303', + "A001", + "A002", + "A003", + "A004", + "A005", + "A006", + "ARG001", + "ARG002", + "ARG003", + "ARG004", + "ARG005", + "ASYNC100", + "ASYNC105", + "ASYNC109", + "ASYNC110", + "ASYNC115", + "ASYNC116", + "ASYNC210", + "ASYNC220", + "ASYNC221", + "ASYNC222", + "ASYNC230", + "ASYNC251", + "B002", + "B003", + "B004", + "B005", + "B006", + "B007", + "B008", + "B009", + "B010", + "B011", + "B012", + "B013", + "B014", + "B015", + "B016", + "B017", + "B018", + "B019", + "B020", + "B021", + "B022", + "B023", + "B024", + "B025", + "B026", + "B028", + "B029", + "B030", + "B031", + "B032", + "B033", + "B034", + "B035", + "B039", + "B904", + "B905", + "B911", + "BLE001", + "C400", + "C401", + "C402", + "C403", + "C404", + "C405", + "C406", + "C408", + "C409", + "C410", + "C411", + "C413", + "C414", + "C415", + "C416", + "C417", + "C418", + "C419", + "C420", + "COM818", + "DTZ001", + "DTZ002", + "DTZ003", + "DTZ004", + "DTZ005", + "DTZ006", + "DTZ007", + "DTZ011", + "DTZ012", + "DTZ901", + "E101", + "E401", + "E402", + "E701", + "E702", + "E703", + "E711", + "E712", + "E713", + "E714", + "E721", + "E722", + "E731", + "E741", + "E742", + "E743", + "E902", + "EM101", + "EM102", + "EM103", + "EXE001", + "EXE002", + "EXE003", + "EXE004", + "EXE005", + "F401", + "F402", + "F403", + "F404", + "F405", + "F406", + "F407", + "F501", + "F502", + "F503", + "F504", + "F505", + "F506", + "F507", + "F508", + "F509", + "F521", + "F522", + "F523", + "F524", + "F525", + "F541", + "F601", + "F602", + "F621", + "F622", + "F631", + "F632", + "F633", + "F634", + "F701", + "F702", + "F704", + "F706", + "F707", + "F722", + "F811", + "F821", + "F822", + "F823", + "F841", + "F842", + "F901", + "FA100", + "FA102", + "FAST001", + "FAST002", + "FAST003", + "FBT001", + "FBT002", + "FLY002", + "FURB105", + "FURB116", + "FURB122", + "FURB129", + "FURB132", + "FURB136", + "FURB157", + "FURB161", + "FURB162", + "FURB163", + "FURB166", + "FURB167", + "FURB168", + "FURB169", + "FURB177", + "FURB181", + "FURB187", + "FURB188", + "G001", + "G002", + "G003", + "G004", + "G010", + "G101", + "G201", + "G202", + "I001", + "I002", + "ICN001", + "ICN002", + "ICN003", + "INP001", + "INT001", + "INT002", + "INT003", + "ISC003", + "LOG001", + "LOG002", + "LOG007", + "LOG009", + "LOG014", + "LOG015", + "N801", + "N802", + "N803", + "N804", + "N805", + "N806", + "N807", + "N811", + "N812", + "N813", + "N814", + "N815", + "N816", + "N817", + "N818", + "N999", + "PERF101", + "PERF102", + "PERF401", + "PERF402", + "PERF403", + "PGH005", + "PIE790", + "PIE794", + "PIE796", + "PIE800", + "PIE804", + "PIE807", + "PIE808", + "PIE810", + "PLC0105", + "PLC0131", + "PLC0132", + "PLC0205", + "PLC0206", + "PLC0208", + "PLC0414", + "PLC0415", + "PLC1802", + "PLC2401", + "PLC2403", + "PLC3002", + "PLE0100", + "PLE0101", + "PLE0115", + "PLE0116", + "PLE0117", + "PLE0118", + "PLE0237", + "PLE0241", + "PLE0302", + "PLE0303", + "PLE0305", + "PLE0307", + "PLE0308", + "PLE0309", + "PLE0604", + "PLE0605", + "PLE0643", + "PLE0704", + "PLE1132", + "PLE1142", + "PLE1205", + "PLE1206", + "PLE1300", + "PLE1307", + "PLE1310", + "PLE1507", + "PLE1519", + "PLE1520", + "PLE1700", + "PLE2502", + "PLE2510", + "PLE2512", + "PLE2513", + "PLE2514", + "PLE2515", + "PLR0124", + "PLR0133", + "PLR0206", + "PLR0402", + "PLR1704", + "PLR1711", + "PLR1714", + "PLR1716", + "PLR1722", + "PLR1730", + "PLR1733", + "PLR1736", + "PLR2004", + "PLR2044", + "PLR5501", + "PLW0120", + "PLW0127", + "PLW0128", + "PLW0129", + "PLW0131", + "PLW0133", + "PLW0177", + "PLW0211", + "PLW0245", + "PLW0406", + "PLW0602", + "PLW0603", + "PLW0604", + "PLW0642", + "PLW0711", + "PLW1501", + "PLW1507", + "PLW1508", + "PLW1509", + "PLW1510", + "PLW1641", + "PLW2101", + "PLW2901", + "PLW3301", + "PT001", + "PT002", + "PT003", + "PT006", + "PT007", + "PT008", + "PT009", + "PT010", + "PT011", + "PT012", + "PT013", + "PT014", + "PT015", + "PT016", + "PT017", + "PT018", + "PT019", + "PT020", + "PT021", + "PT022", + "PT023", + "PT024", + "PT025", + "PT026", + "PT027", + "PT028", + "PT030", + "PT031", + "PYI001", + "PYI002", + "PYI003", + "PYI004", + "PYI005", + "PYI006", + "PYI007", + "PYI008", + "PYI009", + "PYI010", + "PYI011", + "PYI012", + "PYI013", + "PYI014", + "PYI015", + "PYI016", + "PYI017", + "PYI018", + "PYI019", + "PYI020", + "PYI021", + "PYI024", + "PYI025", + "PYI026", + "PYI029", + "PYI030", + "PYI032", + "PYI033", + "PYI034", + "PYI035", + "PYI036", + "PYI041", + "PYI042", + "PYI043", + "PYI044", + "PYI045", + "PYI046", + "PYI047", + "PYI048", + "PYI049", + "PYI050", + "PYI051", + "PYI052", + "PYI053", + "PYI054", + "PYI055", + "PYI056", + "PYI057", + "PYI058", + "PYI059", + "PYI061", + "PYI062", + "PYI063", + "PYI064", + "PYI066", + "RET503", + "RET504", + "RET505", + "RET506", + "RET507", + "RET508", + "RSE102", + "RUF001", + "RUF002", + "RUF003", + "RUF005", + "RUF006", + "RUF007", + "RUF008", + "RUF009", + "RUF010", + "RUF012", + "RUF013", + "RUF015", + "RUF016", + "RUF017", + "RUF018", + "RUF019", + "RUF020", + "RUF021", + "RUF022", + "RUF023", + "RUF024", + "RUF026", + "RUF028", + "RUF030", + "RUF032", + "RUF033", + "RUF034", + "RUF040", + "RUF041", + "RUF043", + "RUF046", + "RUF048", + "RUF049", + "RUF051", + "RUF053", + "RUF057", + "RUF058", + "RUF059", + "RUF100", + "RUF101", + "S101", + "S102", + "S103", + "S104", + "S105", + "S106", + "S107", + "S108", + "S110", + "S112", + "S113", + "S201", + "S202", + "S301", + "S302", + "S303", + "S304", + "S305", + "S306", + "S307", + "S308", + "S310", + "S311", + "S312", + "S313", + "S314", + "S315", + "S316", + "S317", + "S318", + "S319", + "S321", + "S323", + "S324", + "S501", + "S502", + "S503", + "S504", + "S505", + "S506", + "S507", + "S508", + "S509", + "S601", + "S602", + "S604", + "S605", + "S606", + "S607", + "S608", + "S609", + "S610", + "S611", + "S612", + "S701", + "S702", + "S704", + "SIM101", + "SIM102", + "SIM103", + "SIM105", + "SIM107", + "SIM108", + "SIM109", + "SIM110", + "SIM112", + "SIM113", + "SIM114", + "SIM115", + "SIM116", + "SIM117", + "SIM118", + "SIM201", + "SIM202", + "SIM208", + "SIM210", + "SIM211", + "SIM212", + "SIM220", + "SIM221", + "SIM222", + "SIM223", + "SIM300", + "SIM905", + "SIM910", + "SIM911", + "SLF001", + "SLOT000", + "SLOT001", + "SLOT002", + "T100", + "T201", + "T203", + "TC001", + "TC002", + "TC003", + "TC004", + "TC005", + "TC006", + "TC007", + "TC010", + "TD004", + "TD005", + "TD006", + "TD007", + "TID251", + "TID252", + "TID253", + "TRY002", + "TRY003", + "TRY004", + "TRY201", + "TRY203", + "TRY300", + "TRY301", + "TRY400", + "TRY401", + "UP001", + "UP003", + "UP004", + "UP005", + "UP006", + "UP007", + "UP008", + "UP009", + "UP010", + "UP011", + "UP012", + "UP013", + "UP014", + "UP015", + "UP017", + "UP018", + "UP019", + "UP020", + "UP021", + "UP022", + "UP023", + "UP024", + "UP025", + "UP026", + "UP028", + "UP029", + "UP030", + "UP031", + "UP032", + "UP033", + "UP034", + "UP035", + "UP036", + "UP037", + "UP039", + "UP040", + "UP041", + "UP043", + "UP044", + "UP045", + "UP046", + "UP047", + "UP049", + "UP050", + "W291", + "W292", + "W293", + "W505", + "W605", + "YTT101", + "YTT102", + "YTT103", + "YTT201", + "YTT202", + "YTT203", + "YTT204", + "YTT301", + "YTT302", + "YTT303", ) PREVIEW_RULES: tuple[str, ...] = ( - 'A004', - 'A005', - 'A006', - 'ASYNC116', - 'B039', - 'B901', - 'B909', - 'C420', - 'DOC201', - 'DOC202', - 'DOC402', - 'DOC403', - 'DOC501', - 'DOC502', - 'E112', - 'E113', - 'E115', - 'E116', - 'E201', - 'E202', - 'E203', - 'E204', - 'E211', - 'E221', - 'E222', - 'E223', - 'E224', - 'E225', - 'E226', - 'E227', - 'E228', - 'E231', - 'E241', - 'E242', - 'E251', - 'E252', - 'E261', - 'E262', - 'E265', - 'E266', - 'E271', - 'E272', - 'E273', - 'E274', - 'E275', - 'E502', - 'FAST001', - 'FAST002', - 'FAST003', - 'FURB110', - 'FURB113', - 'FURB116', - 'FURB118', - 'FURB131', - 'FURB132', - 'FURB142', - 'FURB145', - 'FURB148', - 'FURB152', - 'FURB154', - 'FURB157', - 'FURB164', - 'FURB166', - 'FURB171', - 'FURB180', - 'FURB188', - 'FURB192', - 'PLC0206', - 'PLC0415', - 'PLC1901', - 'PLC2701', - 'PLC2801', - 'PLE0304', - 'PLE1141', - 'PLE4703', - 'PLR0202', - 'PLR0203', - 'PLR1733', - 'PLR6104', - 'PLR6201', - 'PLR6301', - 'PLW0108', - 'PLW0177', - 'PLW1514', - 'PLW1641', - 'PLW3201', - 'PYI059', - 'PYI063', - 'PYI064', - 'PYI066', - 'RUF021', - 'RUF022', - 'RUF023', - 'RUF027', - 'RUF028', - 'RUF029', - 'RUF030', - 'RUF031', - 'RUF032', - 'RUF033', - 'RUF034', - 'S401', - 'S402', - 'S403', - 'S405', - 'S406', - 'S407', - 'S408', - 'S409', - 'S411', - 'S412', - 'S413', - 'S415', - 'UP042', - 'UP043', - 'W391', + "ASYNC212", + "ASYNC240", + "ASYNC250", + "B901", + "B903", + "B909", + "B912", + "DOC201", + "DOC202", + "DOC402", + "DOC403", + "DOC501", + "DOC502", + "E112", + "E113", + "E115", + "E116", + "E201", + "E202", + "E203", + "E204", + "E211", + "E221", + "E222", + "E223", + "E224", + "E225", + "E226", + "E227", + "E228", + "E231", + "E241", + "E242", + "E251", + "E252", + "E261", + "E262", + "E265", + "E266", + "E271", + "E272", + "E273", + "E274", + "E275", + "E502", + "FURB110", + "FURB113", + "FURB118", + "FURB131", + "FURB142", + "FURB145", + "FURB148", + "FURB152", + "FURB154", + "FURB156", + "FURB164", + "FURB171", + "FURB180", + "FURB189", + "FURB192", + "LOG004", + "PLC0207", + "PLC1901", + "PLC2701", + "PLC2801", + "PLE0304", + "PLE1141", + "PLE4703", + "PLR0202", + "PLR0203", + "PLR6104", + "PLR6201", + "PLR6301", + "PLW0108", + "PLW0244", + "PLW1514", + "PLW3201", + "PT029", + "RUF027", + "RUF029", + "RUF031", + "RUF036", + "RUF037", + "RUF038", + "RUF039", + "RUF045", + "RUF047", + "RUF052", + "RUF054", + "RUF055", + "RUF056", + "RUF060", + "RUF061", + "RUF063", + "RUF064", + "RUF065", + "RUF102", + "S401", + "S402", + "S403", + "S405", + "S406", + "S407", + "S408", + "S409", + "S411", + "S412", + "S413", + "S415", + "TC008", + "UP042", + "W391", ) PER_FILE_IGNORED_RULES: dict[str, list[str]] = { - '**/scripts/*': [ - 'INP001', - 'T201', + "**/scripts/*": [ + "INP001", + "T201", ], - '**/tests/**/*': [ - 'PLC1901', - 'PLR2004', - 'PLR6301', - 'S', - 'TID252', + "**/tests/**/*": [ + "PLC1901", + "PLR2004", + "PLR6301", + "S", + "TID252", ], } diff --git a/src/hatch/cli/new/__init__.py b/src/hatch/cli/new/__init__.py index f1f2d21dd..ca295fa0d 100644 --- a/src/hatch/cli/new/__init__.py +++ b/src/hatch/cli/new/__init__.py @@ -1,13 +1,13 @@ import click -@click.command(short_help='Create or initialize a project') -@click.argument('name', required=False) -@click.argument('location', required=False) -@click.option('--interactive', '-i', 'interactive', is_flag=True, help='Interactively choose details about the project') -@click.option('--cli', 'feature_cli', is_flag=True, help='Give the project a command line interface') -@click.option('--init', 'initialize', is_flag=True, help='Initialize an existing project') -@click.option('-so', 'setuptools_options', multiple=True, hidden=True) +@click.command(short_help="Create or initialize a project") +@click.argument("name", required=False) +@click.argument("location", required=False) +@click.option("--interactive", "-i", "interactive", is_flag=True, help="Interactively choose details about the project") +@click.option("--cli", "feature_cli", is_flag=True, help="Give the project a command line interface") +@click.option("--init", "initialize", is_flag=True, help="Initialize an existing project") +@click.option("-so", "setuptools_options", multiple=True, hidden=True) @click.pass_obj def new(app, name, location, interactive, feature_cli, initialize, setuptools_options): """Create or initialize a project.""" @@ -27,16 +27,16 @@ def new(app, name, location, interactive, feature_cli, initialize, setuptools_op if initialize: interactive = True location = location or Path.cwd() - if (location / 'setup.py').is_file() or (location / 'setup.cfg').is_file(): + if (location / "setup.py").is_file() or (location / "setup.cfg").is_file(): migration_possible = True if not name: - name = 'temporary' + name = "temporary" if not name: if not interactive: - app.abort('Missing required argument for the project name, use the -i/--interactive flag.') + app.abort("Missing required argument for the project name, use the -i/--interactive flag.") - name = app.prompt('Project name') + name = app.prompt("Project name") normalized_name = Project.canonicalize_name(name, strict=False) if not location: @@ -44,56 +44,57 @@ def new(app, name, location, interactive, feature_cli, initialize, setuptools_op needs_config_update = False if location.is_file(): - app.abort(f'Path `{location}` points to a file.') + app.abort(f"Path `{location}` points to a file.") elif location.is_dir() and any(location.iterdir()): if not initialize: - app.abort(f'Directory `{location}` is not empty.') + app.abort(f"Directory `{location}` is not empty.") - needs_config_update = (location / 'pyproject.toml').is_file() + needs_config_update = (location / "pyproject.toml").is_file() if migration_possible: from hatch.cli.new.migrate import migrate from hatch.venv.core import TempVirtualEnv try: - with app.status('Migrating project metadata from setuptools'), TempVirtualEnv( - sys.executable, app.platform - ) as venv: - app.platform.run_command(['python', '-m', 'pip', 'install', '-q', 'setuptools']) + with ( + app.status("Migrating project metadata from setuptools"), + TempVirtualEnv(sys.executable, app.platform) as venv, + ): + app.platform.run_command(["python", "-m", "pip", "install", "-q", "setuptools"]) migrate(str(location), setuptools_options, venv.sys_path) except Exception as e: # noqa: BLE001 - app.display_error(f'Could not automatically migrate from setuptools: {e}') - if name == 'temporary': - name = app.prompt('Project name') + app.display_error(f"Could not automatically migrate from setuptools: {e}") + if name == "temporary": + name = app.prompt("Project name") else: return default_config = { - 'description': '', - 'dependencies': set(), - 'package_name': normalized_name.replace('-', '_'), - 'project_name': name, - 'project_name_normalized': normalized_name, - 'args': {'cli': feature_cli}, + "description": "", + "dependencies": set(), + "package_name": normalized_name.replace("-", "_"), + "project_name": name, + "project_name_normalized": normalized_name, + "args": {"cli": feature_cli}, } if interactive: - default_config['description'] = app.prompt('Description', default='') + default_config["description"] = app.prompt("Description", default="") app.display_info() if needs_config_update: - app.project.initialize(str(location / 'pyproject.toml'), default_config) - app.display_success('Updated: pyproject.toml') + app.project.initialize(str(location / "pyproject.toml"), default_config) + app.display_success("Updated: pyproject.toml") return template_config = deepcopy(app.config.template.raw_data) - if 'plugins' in template_config and not template_config['plugins']: - del template_config['plugins'] + if "plugins" in template_config and not template_config["plugins"]: + del template_config["plugins"] - TemplateConfig(template_config, ('template',)).parse_fields() + TemplateConfig(template_config, ("template",)).parse_fields() - plugin_config = template_config.pop('plugins') + plugin_config = template_config.pop("plugins") # Set up default config for template files template_config.update(default_config) @@ -108,9 +109,9 @@ def new(app, name, location, interactive, feature_cli, initialize, setuptools_op ) if not templates: - app.abort(f'None of the defined plugins were found: {", ".join(sorted(plugin_config))}') + app.abort(f"None of the defined plugins were found: {', '.join(sorted(plugin_config))}") elif plugin_config: - app.abort(f'Some of the defined plugins were not found: {", ".join(sorted(plugin_config))}') + app.abort(f"Some of the defined plugins were not found: {', '.join(sorted(plugin_config))}") for template in templates: template.initialize_config(template_config) @@ -124,7 +125,7 @@ def new(app, name, location, interactive, feature_cli, initialize, setuptools_op if possible_template_file.__class__ is not File else possible_template_file ) - if template_file.path is None or (initialize and str(template_file.path) != 'pyproject.toml'): + if template_file.path is None or (initialize and str(template_file.path) != "pyproject.toml"): continue template_files.append(template_file) @@ -136,7 +137,7 @@ def new(app, name, location, interactive, feature_cli, initialize, setuptools_op template_file.write(location) if initialize: - app.display_success('Wrote: pyproject.toml') + app.display_success("Wrote: pyproject.toml") return from rich.markup import escape @@ -147,14 +148,14 @@ def recurse_directory(directory, tree): for path in paths: if path.is_dir(): recurse_directory( - path, tree.add(f'[bold magenta][link={app.platform.format_file_uri(path)}]{escape(path.name)}') + path, tree.add(f"[bold magenta][link={app.platform.format_file_uri(path)}]{escape(path.name)}") ) else: - tree.add(f'[green][link={app.platform.format_file_uri(path)}]{escape(path.name)}') + tree.add(f"[green][link={app.platform.format_file_uri(path)}]{escape(path.name)}") root = Tree( - f'[bold magenta][link={app.platform.format_file_uri(location)}]{escape(location.name)}', - guide_style='bright_blue', + f"[bold magenta][link={app.platform.format_file_uri(location)}]{escape(location.name)}", + guide_style="bright_blue", hide_root=location == Path.cwd(), ) recurse_directory(location, root) diff --git a/src/hatch/cli/new/migrate.py b/src/hatch/cli/new/migrate.py index f11aada22..3a4201cc2 100644 --- a/src/hatch/cli/new/migrate.py +++ b/src/hatch/cli/new/migrate.py @@ -3,7 +3,7 @@ FILE = os.path.abspath(__file__) HERE = os.path.dirname(FILE) -ENV_VAR_PREFIX = '_HATCHLING_PORT_ADD_' +ENV_VAR_PREFIX = "_HATCHLING_PORT_ADD_" def _apply_env_vars(kwargs): @@ -11,13 +11,13 @@ def _apply_env_vars(kwargs): for key, value in os.environ.items(): if key.startswith(ENV_VAR_PREFIX): - kwargs[key.replace(ENV_VAR_PREFIX, '', 1).lower()] = literal_eval(value) + kwargs[key.replace(ENV_VAR_PREFIX, "", 1).lower()] = literal_eval(value) def _parse_dependencies(dependency_definition): dependencies = [] for line in dependency_definition.splitlines(): - dependency = line.split(' #', 1)[0].strip() + dependency = line.split(" #", 1)[0].strip() if dependency: dependencies.append(dependency) @@ -28,8 +28,8 @@ def _collapse_data(output, data): import tomli_w expected_output = new_output = tomli_w.dumps(data) - new_output = new_output.replace(' ', '') - new_output = new_output.replace('[\n', '[') + new_output = new_output.replace(" ", "") + new_output = new_output.replace("[\n", "[") new_output = new_output.replace('",\n]', '"]') return output.replace(expected_output, new_output, 1) @@ -38,85 +38,85 @@ def _collapse_data(output, data): def _parse_setup_cfg(kwargs): from configparser import ConfigParser - setup_cfg_file = os.path.join(HERE, 'setup.cfg') + setup_cfg_file = os.path.join(HERE, "setup.cfg") if not os.path.isfile(setup_cfg_file): return setup_cfg = ConfigParser() - setup_cfg.read(setup_cfg_file, encoding='utf-8') + setup_cfg.read(setup_cfg_file, encoding="utf-8") - if not setup_cfg.has_section('metadata'): + if not setup_cfg.has_section("metadata"): return - metadata = setup_cfg['metadata'] + metadata = setup_cfg["metadata"] - if 'name' in metadata and 'name' not in kwargs: - kwargs['name'] = metadata['name'] + if "name" in metadata and "name" not in kwargs: + kwargs["name"] = metadata["name"] - if 'description' in metadata and 'description' not in kwargs: - kwargs['description'] = metadata['description'] + if "description" in metadata and "description" not in kwargs: + kwargs["description"] = metadata["description"] - if 'license' in metadata and 'license' not in kwargs: - kwargs['license'] = metadata['license'] + if "license" in metadata and "license" not in kwargs: + kwargs["license"] = metadata["license"] - if 'author' in metadata and 'author' not in kwargs: - kwargs['author'] = metadata['author'] + if "author" in metadata and "author" not in kwargs: + kwargs["author"] = metadata["author"] - if 'author_email' in metadata and 'author_email' not in kwargs: - kwargs['author_email'] = metadata['author_email'] + if "author_email" in metadata and "author_email" not in kwargs: + kwargs["author_email"] = metadata["author_email"] - if 'keywords' in metadata and 'keywords' not in kwargs: - keywords = metadata['keywords'].strip().splitlines() - kwargs['keywords'] = keywords if len(keywords) > 1 else keywords[0] + if "keywords" in metadata and "keywords" not in kwargs: + keywords = metadata["keywords"].strip().splitlines() + kwargs["keywords"] = keywords if len(keywords) > 1 else keywords[0] - if 'classifiers' in metadata and 'classifiers' not in kwargs: - kwargs['classifiers'] = metadata['classifiers'].strip().splitlines() + if "classifiers" in metadata and "classifiers" not in kwargs: + kwargs["classifiers"] = metadata["classifiers"].strip().splitlines() - if 'url' in metadata and 'url' not in kwargs: - kwargs['url'] = metadata['url'] + if "url" in metadata and "url" not in kwargs: + kwargs["url"] = metadata["url"] - if 'download_url' in metadata and 'download_url' not in kwargs: - kwargs['download_url'] = metadata['download_url'] + if "download_url" in metadata and "download_url" not in kwargs: + kwargs["download_url"] = metadata["download_url"] - if 'project_urls' in metadata and 'project_urls' not in kwargs: - kwargs['project_urls'] = dict( - url.replace(' = ', '=', 1).split('=') for url in metadata['project_urls'].strip().splitlines() + if "project_urls" in metadata and "project_urls" not in kwargs: + kwargs["project_urls"] = dict( + url.replace(" = ", "=", 1).split("=") for url in metadata["project_urls"].strip().splitlines() ) - if setup_cfg.has_section('options'): - options = setup_cfg['options'] + if setup_cfg.has_section("options"): + options = setup_cfg["options"] - if 'python_requires' in options and 'python_requires' not in kwargs: - kwargs['python_requires'] = options['python_requires'] + if "python_requires" in options and "python_requires" not in kwargs: + kwargs["python_requires"] = options["python_requires"] - if 'install_requires' in options and 'install_requires' not in kwargs: - kwargs['install_requires'] = _parse_dependencies(options['install_requires']) + if "install_requires" in options and "install_requires" not in kwargs: + kwargs["install_requires"] = _parse_dependencies(options["install_requires"]) - if 'packages' in options and 'packages' not in kwargs: + if "packages" in options and "packages" not in kwargs: packages = [] - for package_spec in options['packages'].strip().splitlines(): - package = package_spec.replace('find:', '', 1).replace('find_namespace:', '', 1).strip() + for package_spec in options["packages"].strip().splitlines(): + package = package_spec.replace("find:", "", 1).replace("find_namespace:", "", 1).strip() if package: packages.append(package) if packages: - kwargs['packages'] = packages + kwargs["packages"] = packages - if 'package_dir' in options and 'package_dir' not in kwargs: - kwargs['package_dir'] = dict( - d.replace(' = ', '=', 1).split('=') for d in options['package_dir'].strip().splitlines() + if "package_dir" in options and "package_dir" not in kwargs: + kwargs["package_dir"] = dict( + d.replace(" = ", "=", 1).split("=") for d in options["package_dir"].strip().splitlines() ) - if setup_cfg.has_section('options.extras_require') and 'extras_require' not in kwargs: - kwargs['extras_require'] = { + if setup_cfg.has_section("options.extras_require") and "extras_require" not in kwargs: + kwargs["extras_require"] = { feature: _parse_dependencies(dependencies) - for feature, dependencies in setup_cfg['options.extras_require'].items() + for feature, dependencies in setup_cfg["options.extras_require"].items() } - if setup_cfg.has_section('options.entry_points') and 'entry_points' not in kwargs: - kwargs['entry_points'] = { + if setup_cfg.has_section("options.entry_points") and "entry_points" not in kwargs: + kwargs["entry_points"] = { entry_point: definitions.strip().splitlines() - for entry_point, definitions in setup_cfg['options.entry_points'].items() + for entry_point, definitions in setup_cfg["options.entry_points"].items() } @@ -128,151 +128,151 @@ def setup(**kwargs): project_metadata = {} hatch_metadata = {} - tool_metadata = {'hatch': hatch_metadata} + tool_metadata = {"hatch": hatch_metadata} project_data = { - 'build-system': {'requires': ['hatchling'], 'build-backend': 'hatchling.build'}, - 'project': project_metadata, - 'tool': tool_metadata, + "build-system": {"requires": ["hatchling"], "build-backend": "hatchling.build"}, + "project": project_metadata, + "tool": tool_metadata, } _parse_setup_cfg(kwargs) _apply_env_vars(kwargs) - name = kwargs['name'] - project_name = name.replace('_', '-') - packages = sorted(kwargs.get('packages') or [name.replace('-', '_')]) - package_name = package_path = package_source = packages[0].split('.')[0].lower().strip() + name = kwargs["name"] + project_name = name.replace("_", "-") + packages = sorted(kwargs.get("packages") or [name.replace("-", "_")]) + package_name = package_path = package_source = packages[0].split(".")[0].lower().strip() - project_metadata['name'] = project_name + project_metadata["name"] = project_name - project_metadata['dynamic'] = ['version'] + project_metadata["dynamic"] = ["version"] - if 'description' in kwargs: - project_metadata['description'] = kwargs['description'] + if "description" in kwargs: + project_metadata["description"] = kwargs["description"] - for readme_file in ('README.md', 'README.rst', 'README.txt'): + for readme_file in ("README.md", "README.rst", "README.txt"): if os.path.isfile(os.path.join(HERE, readme_file)): - project_metadata['readme'] = readme_file + project_metadata["readme"] = readme_file break - project_metadata['license'] = kwargs.get('license', '') + project_metadata["license"] = kwargs.get("license", "") - if 'python_requires' in kwargs: - project_metadata['requires-python'] = kwargs['python_requires'] + if "python_requires" in kwargs: + project_metadata["requires-python"] = kwargs["python_requires"] - for collaborator in ('author', 'maintainer'): + for collaborator in ("author", "maintainer"): collaborators = [] collaborator_names = [] collaborator_emails = [] if collaborator in kwargs: collaborator_names.extend( - collaborator_name.strip() for collaborator_name in kwargs[collaborator].split(',') + collaborator_name.strip() for collaborator_name in kwargs[collaborator].split(",") ) - if f'{collaborator}_email' in kwargs: + if f"{collaborator}_email" in kwargs: collaborator_emails.extend( - collaborator_email.strip() for collaborator_email in kwargs[f'{collaborator}_email'].split(',') + collaborator_email.strip() for collaborator_email in kwargs[f"{collaborator}_email"].split(",") ) for collaborator_name, collaborator_email in itertools.zip_longest(collaborator_names, collaborator_emails): data = {} if collaborator_name is not None: - data['name'] = collaborator_name + data["name"] = collaborator_name if collaborator_email is not None: - data['email'] = collaborator_email + data["email"] = collaborator_email if data: collaborators.append(data) if collaborators: - project_metadata[f'{collaborator}s'] = collaborators + project_metadata[f"{collaborator}s"] = collaborators - if 'keywords' in kwargs: - keywords = kwargs['keywords'] + if "keywords" in kwargs: + keywords = kwargs["keywords"] if isinstance(keywords, str): - keywords = keywords.replace(',', ' ').split() - project_metadata['keywords'] = sorted(keywords) + keywords = keywords.replace(",", " ").split() + project_metadata["keywords"] = sorted(keywords) - if 'classifiers' in kwargs: - project_metadata['classifiers'] = sorted(kwargs['classifiers']) + if "classifiers" in kwargs: + project_metadata["classifiers"] = sorted(kwargs["classifiers"]) fixed_indices = [] final_index = 0 - for i, classifier in enumerate(project_metadata['classifiers']): - if classifier.startswith('Programming Language :: Python :: '): + for i, classifier in enumerate(project_metadata["classifiers"]): + if classifier.startswith("Programming Language :: Python :: "): final_index = i - for python_version in ('3.10', '3.11', '3.12'): + for python_version in ("3.10", "3.11", "3.12"): if classifier.endswith(python_version): fixed_indices.append(i) break for i, index in enumerate(fixed_indices): - project_metadata['classifiers'].insert(final_index, project_metadata['classifiers'].pop(index - i)) + project_metadata["classifiers"].insert(final_index, project_metadata["classifiers"].pop(index - i)) - if 'install_requires' in kwargs: - project_metadata['dependencies'] = sorted( - [entry.strip() for entry in kwargs['install_requires']] - if isinstance(kwargs['install_requires'], (list, tuple)) - else _parse_dependencies(kwargs['install_requires']), + if "install_requires" in kwargs: + project_metadata["dependencies"] = sorted( + [entry.strip() for entry in kwargs["install_requires"]] + if isinstance(kwargs["install_requires"], (list, tuple)) + else _parse_dependencies(kwargs["install_requires"]), key=lambda d: d.lower(), ) - if 'extras_require' in kwargs: - project_metadata['optional-dependencies'] = { + if "extras_require" in kwargs: + project_metadata["optional-dependencies"] = { group: sorted( [entry.strip() for entry in dependencies] if isinstance(dependencies, (list, tuple)) else _parse_dependencies(dependencies), key=lambda d: d.lower(), ) - for group, dependencies in sorted(kwargs['extras_require'].items()) + for group, dependencies in sorted(kwargs["extras_require"].items()) } - if 'entry_points' in kwargs and isinstance(kwargs['entry_points'], dict): + if "entry_points" in kwargs and isinstance(kwargs["entry_points"], dict): entry_points = {} - for entry_point, raw_definitions in kwargs['entry_points'].items(): + for entry_point, raw_definitions in kwargs["entry_points"].items(): definitions = [raw_definitions] if isinstance(raw_definitions, str) else raw_definitions - definitions = dict(sorted(d.replace(' ', '').split('=', 1) for d in definitions)) + definitions = dict(sorted(d.replace(" ", "").split("=", 1) for d in definitions)) - if entry_point == 'console_scripts': - project_metadata['scripts'] = definitions - elif entry_point == 'gui_scripts': - project_metadata['gui-scripts'] = definitions + if entry_point == "console_scripts": + project_metadata["scripts"] = definitions + elif entry_point == "gui_scripts": + project_metadata["gui-scripts"] = definitions else: entry_points[entry_point] = definitions if entry_points: - project_metadata['entry-points'] = dict(sorted(entry_points.items())) + project_metadata["entry-points"] = dict(sorted(entry_points.items())) urls = {} - if 'url' in kwargs: - urls['Homepage'] = kwargs['url'] - if 'download_url' in kwargs: - urls['Download'] = kwargs['download_url'] - if 'project_urls' in kwargs: - urls.update(kwargs['project_urls']) + if "url" in kwargs: + urls["Homepage"] = kwargs["url"] + if "download_url" in kwargs: + urls["Download"] = kwargs["download_url"] + if "project_urls" in kwargs: + urls.update(kwargs["project_urls"]) if urls: - project_metadata['urls'] = dict(sorted(urls.items())) + project_metadata["urls"] = dict(sorted(urls.items())) build_targets = {} build_data = {} - if 'use_scm_version' in kwargs: - project_data['build-system']['requires'].append('hatch-vcs') - hatch_metadata['version'] = {'source': 'vcs'} - build_data['hooks'] = {'vcs': {'version-file': f'{package_path}/_version.py'}} + if "use_scm_version" in kwargs: + project_data["build-system"]["requires"].append("hatch-vcs") + hatch_metadata["version"] = {"source": "vcs"} + build_data["hooks"] = {"vcs": {"version-file": f"{package_path}/_version.py"}} else: - hatch_metadata['version'] = {'path': f'{package_path}/__init__.py'} + hatch_metadata["version"] = {"path": f"{package_path}/__init__.py"} - build_data['targets'] = build_targets + build_data["targets"] = build_targets - if '' in kwargs.get('package_dir', {}): - package_source = kwargs['package_dir'][''] + if "" in kwargs.get("package_dir", {}): + package_source = kwargs["package_dir"][""] - package = (kwargs.get('packages') or [package_name])[0] - package_path = f'{package_source}/{package}' + package = (kwargs.get("packages") or [package_name])[0] + package_path = f"{package_source}/{package}" - if package_path != f'src/{package_name}': - build_targets.setdefault('wheel', {})['packages'] = [package_path] + if package_path != f"src/{package_name}": + build_targets.setdefault("wheel", {})["packages"] = [package_path] - if kwargs.get('data_files', []): + if kwargs.get("data_files", []): shared_data = {} - for shared_directory, relative_paths in kwargs['data_files']: + for shared_directory, relative_paths in kwargs["data_files"]: relative_files = {} for relative_path in relative_paths: relative_directory, filename = os.path.split(relative_path) @@ -281,48 +281,48 @@ def setup(**kwargs): for relative_directory, files in sorted(relative_files.items()): if not os.path.isdir(relative_directory) or set(os.listdir(relative_directory)) != set(files): for filename in sorted(files): - local_path = os.path.join(relative_directory, filename).replace('\\', '/') - shared_data[local_path] = f'{shared_directory}/{filename}' + local_path = os.path.join(relative_directory, filename).replace("\\", "/") + shared_data[local_path] = f"{shared_directory}/{filename}" else: shared_data[relative_directory] = shared_directory - build_targets.setdefault('wheel', {})['shared-data'] = shared_data + build_targets.setdefault("wheel", {})["shared-data"] = shared_data - build_targets['sdist'] = { - 'include': [ - f'/{package_source}', + build_targets["sdist"] = { + "include": [ + f"/{package_source}", ] } - hatch_metadata['build'] = build_data + hatch_metadata["build"] = build_data output = tomli_w.dumps(project_data) - output = _collapse_data(output, {'requires': project_data['build-system']['requires']}) - output = _collapse_data(output, {'dynamic': project_data['project']['dynamic']}) + output = _collapse_data(output, {"requires": project_data["build-system"]["requires"]}) + output = _collapse_data(output, {"dynamic": project_data["project"]["dynamic"]}) - project_file = os.path.join(HERE, 'pyproject.toml') + project_file = os.path.join(HERE, "pyproject.toml") if os.path.isfile(project_file): - with open(project_file, encoding='utf-8') as f: + with open(project_file, encoding="utf-8") as f: current_contents = f.read() - for section in ('build-system', 'project'): - for pattern in (rf'^\[{section}].*?(?=^\[)', rf'^\[{section}].*'): - current_contents = re.sub(pattern, '', current_contents, flags=re.MULTILINE | re.DOTALL) + for section in ("build-system", "project"): + for pattern in (rf"^\[{section}].*?(?=^\[)", rf"^\[{section}].*"): + current_contents = re.sub(pattern, "", current_contents, flags=re.MULTILINE | re.DOTALL) - output += f'\n{current_contents}' + output += f"\n{current_contents}" - with open(project_file, 'w', encoding='utf-8') as f: + with open(project_file, "w", encoding="utf-8") as f: f.write(output) -if __name__ == 'setuptools': - __this_shim = sys.modules.pop('setuptools') +if __name__ == "setuptools": + __this_shim = sys.modules.pop("setuptools") __current_directory = sys.path.pop(0) import setuptools as __real_setuptools sys.path.insert(0, __current_directory) - sys.modules['setuptools'] = __this_shim + sys.modules["setuptools"] = __this_shim def __getattr__(name): return getattr(__real_setuptools, name) @@ -337,31 +337,31 @@ def migrate(root, setuptools_options, sys_paths): from tempfile import TemporaryDirectory with TemporaryDirectory() as temp_dir: - repo_dir = os.path.join(os.path.realpath(temp_dir), 'repo') - shutil.copytree(root, repo_dir, ignore=shutil.ignore_patterns('.git', '.tox'), copy_function=shutil.copy) - shutil.copy(FILE, os.path.join(repo_dir, 'setuptools.py')) - setup_py = os.path.join(repo_dir, 'setup.py') + repo_dir = os.path.join(os.path.realpath(temp_dir), "repo") + shutil.copytree(root, repo_dir, ignore=shutil.ignore_patterns(".git", ".tox"), copy_function=shutil.copy) + shutil.copy(FILE, os.path.join(repo_dir, "setuptools.py")) + setup_py = os.path.join(repo_dir, "setup.py") if not os.path.isfile(setup_py): # Synthesize a small setup.py file since there is none - with open(setup_py, 'w', encoding='utf-8') as f: - f.write('from setuptools import setup\nsetup()\n') + with open(setup_py, "w", encoding="utf-8") as f: + f.write("from setuptools import setup\nsetup()\n") env = dict(os.environ) for arg in setuptools_options: - key, value = arg.split('=', 1) - env[f'{ENV_VAR_PREFIX}{key}'] = value + key, value = arg.split("=", 1) + env[f"{ENV_VAR_PREFIX}{key}"] = value # When PYTHONSAFEPATH is non-empty, the current directory is not added automatically python_paths = [repo_dir] python_paths.extend(p for p in sys_paths if p) - if python_path := env.get('PYTHONPATH', ''): + if python_path := env.get("PYTHONPATH", ""): python_paths.append(python_path) - env['PYTHONPATH'] = os.pathsep.join(python_paths) + env["PYTHONPATH"] = os.pathsep.join(python_paths) subprocess.run([sys.executable, setup_py], env=env, cwd=repo_dir, check=True) - old_project_file = os.path.join(root, 'pyproject.toml') - new_project_file = os.path.join(repo_dir, 'pyproject.toml') + old_project_file = os.path.join(root, "pyproject.toml") + new_project_file = os.path.join(repo_dir, "pyproject.toml") shutil.copyfile(new_project_file, old_project_file) diff --git a/src/hatch/cli/project/__init__.py b/src/hatch/cli/project/__init__.py index 672936ce4..3ad565bd1 100644 --- a/src/hatch/cli/project/__init__.py +++ b/src/hatch/cli/project/__init__.py @@ -3,7 +3,7 @@ from hatch.cli.project.metadata import metadata -@click.group(short_help='View project information') +@click.group(short_help="View project information") def project(): pass diff --git a/src/hatch/cli/project/metadata.py b/src/hatch/cli/project/metadata.py index 37912f693..60d3918c1 100644 --- a/src/hatch/cli/project/metadata.py +++ b/src/hatch/cli/project/metadata.py @@ -8,8 +8,8 @@ from hatch.cli.application import Application -@click.command(short_help='Display project metadata') -@click.argument('field', required=False) +@click.command(short_help="Display project metadata") +@click.argument("field", required=False) @click.pass_obj def metadata(app: Application, field: str | None): """ @@ -39,12 +39,12 @@ def metadata(app: Application, field: str | None): if field: if field not in project_metadata: - app.abort(f'Unknown metadata field: {field}') - elif field == 'readme': - if project_metadata[field]['content-type'] == 'text/markdown': # no cov - app.display_markdown(project_metadata[field]['text']) + app.abort(f"Unknown metadata field: {field}") + elif field == "readme": + if project_metadata[field]["content-type"] == "text/markdown": # no cov + app.display_markdown(project_metadata[field]["text"]) else: - app.display(project_metadata[field]['text']) + app.display(project_metadata[field]["text"]) elif isinstance(project_metadata[field], str): app.display(project_metadata[field]) else: diff --git a/src/hatch/cli/publish/__init__.py b/src/hatch/cli/publish/__init__.py index c24249842..7e49b5d45 100644 --- a/src/hatch/cli/publish/__init__.py +++ b/src/hatch/cli/publish/__init__.py @@ -3,62 +3,62 @@ from hatch.config.constants import PublishEnvVars -@click.command(short_help='Publish build artifacts') -@click.argument('artifacts', nargs=-1) +@click.command(short_help="Publish build artifacts") +@click.argument("artifacts", nargs=-1) @click.option( - '--repo', - '-r', + "--repo", + "-r", envvar=PublishEnvVars.REPO, - help='The repository with which to publish artifacts [env var: `HATCH_INDEX_REPO`]', + help="The repository with which to publish artifacts [env var: `HATCH_INDEX_REPO`]", ) @click.option( - '--user', '-u', envvar=PublishEnvVars.USER, help='The user with which to authenticate [env var: `HATCH_INDEX_USER`]' + "--user", "-u", envvar=PublishEnvVars.USER, help="The user with which to authenticate [env var: `HATCH_INDEX_USER`]" ) @click.option( - '--auth', - '-a', + "--auth", + "-a", envvar=PublishEnvVars.AUTH, - help='The credentials to use for authentication [env var: `HATCH_INDEX_AUTH`]', + help="The credentials to use for authentication [env var: `HATCH_INDEX_AUTH`]", ) @click.option( - '--ca-cert', + "--ca-cert", envvar=PublishEnvVars.CA_CERT, - help='The path to a CA bundle [env var: `HATCH_INDEX_CA_CERT`]', + help="The path to a CA bundle [env var: `HATCH_INDEX_CA_CERT`]", ) @click.option( - '--client-cert', + "--client-cert", envvar=PublishEnvVars.CLIENT_CERT, - help='The path to a client certificate, optionally containing the private key [env var: `HATCH_INDEX_CLIENT_CERT`]', + help="The path to a client certificate, optionally containing the private key [env var: `HATCH_INDEX_CLIENT_CERT`]", ) @click.option( - '--client-key', + "--client-key", envvar=PublishEnvVars.CLIENT_KEY, help="The path to the client certificate's private key [env var: `HATCH_INDEX_CLIENT_KEY`]", ) -@click.option('--no-prompt', '-n', is_flag=True, help='Disable prompts, such as for missing required fields') +@click.option("--no-prompt", "-n", is_flag=True, help="Disable prompts, such as for missing required fields") @click.option( - '--initialize-auth', is_flag=True, help='Save first-time authentication information even if nothing was published' + "--initialize-auth", is_flag=True, help="Save first-time authentication information even if nothing was published" ) @click.option( - '--publisher', - '-p', - 'publisher_name', + "--publisher", + "-p", + "publisher_name", envvar=PublishEnvVars.PUBLISHER, - default='index', - help='The publisher plugin to use (default is `index`) [env var: `HATCH_PUBLISHER`]', + default="index", + help="The publisher plugin to use (default is `index`) [env var: `HATCH_PUBLISHER`]", ) @click.option( - '--option', - '-o', - 'options', + "--option", + "-o", + "options", envvar=PublishEnvVars.OPTIONS, multiple=True, help=( - 'Options to pass to the publisher plugin. This may be selected multiple ' - 'times e.g. `-o foo=bar -o baz=23` [env var: `HATCH_PUBLISHER_OPTIONS`]' + "Options to pass to the publisher plugin. This may be selected multiple " + "times e.g. `-o foo=bar -o baz=23` [env var: `HATCH_PUBLISHER_OPTIONS`]" ), ) -@click.option('--yes', '-y', is_flag=True, help='Confirm without prompting when the plugin is disabled') +@click.option("--yes", "-y", is_flag=True, help="Confirm without prompting when the plugin is disabled") @click.pass_obj def publish( app, @@ -76,40 +76,40 @@ def publish( yes, ): """Publish build artifacts.""" - option_map = {'no_prompt': no_prompt, 'initialize_auth': initialize_auth} - if publisher_name == 'index': + option_map = {"no_prompt": no_prompt, "initialize_auth": initialize_auth} + if publisher_name == "index": if options: - app.abort('Use the standard CLI flags rather than passing explicit options when using the `index` plugin') + app.abort("Use the standard CLI flags rather than passing explicit options when using the `index` plugin") if repo: - option_map['repo'] = repo + option_map["repo"] = repo if user: - option_map['user'] = user + option_map["user"] = user if auth: - option_map['auth'] = auth + option_map["auth"] = auth if ca_cert: - option_map['ca_cert'] = ca_cert + option_map["ca_cert"] = ca_cert if client_cert: - option_map['client_cert'] = client_cert + option_map["client_cert"] = client_cert if client_key: - option_map['client_key'] = client_key + option_map["client_key"] = client_key else: # no cov for option in options: - key, _, value = option.partition('=') + key, _, value = option.partition("=") option_map[key] = value publisher_class = app.plugins.publisher.get(publisher_name) if publisher_class is None: - app.abort(f'Unknown publisher: {publisher_name}') + app.abort(f"Unknown publisher: {publisher_name}") publisher = publisher_class( app, app.project.location, - app.cache_dir / 'publish' / publisher_name, + app.cache_dir / "publish" / publisher_name, app.project.config.publish.get(publisher_name, {}), app.config.publish.get(publisher_name, {}), ) - if publisher.disable and not (yes or (not no_prompt and app.confirm(f'Confirm `{publisher_name}` publishing'))): - app.abort(f'Publisher is disabled: {publisher_name}') + if publisher.disable and not (yes or (not no_prompt and app.confirm(f"Confirm `{publisher_name}` publishing"))): + app.abort(f"Publisher is disabled: {publisher_name}") publisher.publish(list(artifacts), option_map) diff --git a/src/hatch/cli/python/__init__.py b/src/hatch/cli/python/__init__.py index d6ceb8e8f..a19f2b257 100644 --- a/src/hatch/cli/python/__init__.py +++ b/src/hatch/cli/python/__init__.py @@ -7,7 +7,7 @@ from hatch.cli.python.update import update -@click.group(short_help='Manage Python installations') +@click.group(short_help="Manage Python installations") def python(): pass diff --git a/src/hatch/cli/python/find.py b/src/hatch/cli/python/find.py index c23b6948c..dc4d70b16 100644 --- a/src/hatch/cli/python/find.py +++ b/src/hatch/cli/python/find.py @@ -8,17 +8,17 @@ from hatch.cli.application import Application -@click.command(short_help='Locate Python binaries') -@click.argument('name') -@click.option('-p', '--parent', is_flag=True, help='Show the parent directory of the Python binary') -@click.option('--dir', '-d', 'directory', help='The directory in which distributions reside') +@click.command(short_help="Locate Python binaries") +@click.argument("name") +@click.option("-p", "--parent", is_flag=True, help="Show the parent directory of the Python binary") +@click.option("--dir", "-d", "directory", help="The directory in which distributions reside") @click.pass_obj def find(app: Application, *, name: str, parent: bool, directory: str | None): """Locate Python binaries.""" manager = app.get_python_manager(directory) installed = manager.get_installed() if name not in installed: - app.abort(f'Distribution not installed: {name}') + app.abort(f"Distribution not installed: {name}") dist = installed[name] app.display(str(dist.python_path.parent if parent else dist.python_path)) diff --git a/src/hatch/cli/python/install.py b/src/hatch/cli/python/install.py index bd7e8adcd..142bc6df6 100644 --- a/src/hatch/cli/python/install.py +++ b/src/hatch/cli/python/install.py @@ -18,12 +18,12 @@ def ensure_path_public(path: str, shells: list[str]) -> bool: return False -@click.command(short_help='Install Python distributions') -@click.argument('names', required=True, nargs=-1) -@click.option('--private', is_flag=True, help='Do not add distributions to the user PATH') -@click.option('--update', '-u', is_flag=True, help='Update existing installations') +@click.command(short_help="Install Python distributions") +@click.argument("names", required=True, nargs=-1) +@click.option("--private", is_flag=True, help="Do not add distributions to the user PATH") +@click.option("--update", "-u", is_flag=True, help="Update existing installations") @click.option( - '--dir', '-d', 'directory', help='The directory in which to install distributions, overriding configuration' + "--dir", "-d", "directory", help="The directory in which to install distributions, overriding configuration" ) @click.pass_obj def install(app: Application, *, names: tuple[str, ...], private: bool, update: bool, directory: str | None): @@ -52,7 +52,7 @@ def install(app: Application, *, names: tuple[str, ...], private: bool, update: manager = app.get_python_manager(directory) installed = manager.get_installed() - selection = ORDERED_DISTRIBUTIONS if 'all' in names else names + selection = ORDERED_DISTRIBUTIONS if "all" in names else names unknown = [] compatible = [] incompatible = [] @@ -71,9 +71,9 @@ def install(app: Application, *, names: tuple[str, ...], private: bool, update: compatible.append(name) if unknown: - app.abort(f'Unknown distributions: {", ".join(unknown)}') - elif incompatible and (not compatible or 'all' not in names): - app.abort(f'Incompatible distributions: {", ".join(incompatible)}') + app.abort(f"Unknown distributions: {', '.join(unknown)}") + elif incompatible and (not compatible or "all" not in names): + app.abort(f"Incompatible distributions: {', '.join(incompatible)}") directories_made_public = [] for name in compatible: @@ -82,26 +82,26 @@ def install(app: Application, *, names: tuple[str, ...], private: bool, update: installed_dist = installed[name] needs_update = installed_dist.needs_update() if not needs_update: - app.display_warning(f'The latest version is already installed: {installed_dist.version}') + app.display_warning(f"The latest version is already installed: {installed_dist.version}") continue - if not (update or app.confirm(f'Update {name}?')): - app.abort(f'Distribution is already installed: {name}') + if not (update or app.confirm(f"Update {name}?")): + app.abort(f"Distribution is already installed: {name}") - with app.status(f'{"Updating" if needs_update else "Installing"} {name}'): + with app.status(f"{'Updating' if needs_update else 'Installing'} {name}"): dist = manager.install(name) if not private: python_directory = str(dist.python_path.parent) if not ensure_path_public(python_directory, shells=shells): directories_made_public.append(python_directory) - app.display_success(f'{"Updated" if needs_update else "Installed"} {name} @ {dist.path}') + app.display_success(f"{'Updated' if needs_update else 'Installed'} {name} @ {dist.path}") if directories_made_public: multiple = len(directories_made_public) > 1 app.display( - f'\nThe following director{"ies" if multiple else "y"} ha{"ve" if multiple else "s"} ' - f'been added to your PATH (pending a shell restart):\n' + f"\nThe following director{'ies' if multiple else 'y'} ha{'ve' if multiple else 's'} " + f"been added to your PATH (pending a shell restart):\n" ) for public_directory in directories_made_public: app.display(public_directory) diff --git a/src/hatch/cli/python/remove.py b/src/hatch/cli/python/remove.py index c3acd06b5..10168d25b 100644 --- a/src/hatch/cli/python/remove.py +++ b/src/hatch/cli/python/remove.py @@ -8,9 +8,9 @@ from hatch.cli.application import Application -@click.command(short_help='Remove Python distributions') -@click.argument('names', required=True, nargs=-1) -@click.option('--dir', '-d', 'directory', help='The directory in which distributions reside') +@click.command(short_help="Remove Python distributions") +@click.argument("names", required=True, nargs=-1) +@click.option("--dir", "-d", "directory", help="The directory in which distributions reside") @click.pass_obj def remove(app: Application, *, names: tuple[str, ...], directory: str | None): """ @@ -25,12 +25,12 @@ def remove(app: Application, *, names: tuple[str, ...], directory: str | None): """ manager = app.get_python_manager(directory) installed = manager.get_installed() - selection = tuple(installed) if 'all' in names else names + selection = tuple(installed) if "all" in names else names for name in selection: if name not in installed: - app.display_warning(f'Distribution is not installed: {name}') + app.display_warning(f"Distribution is not installed: {name}") continue dist = installed[name] - with app.status(f'Removing {name}'): + with app.status(f"Removing {name}"): manager.remove(dist) diff --git a/src/hatch/cli/python/show.py b/src/hatch/cli/python/show.py index f9e31232d..8c79f29c5 100644 --- a/src/hatch/cli/python/show.py +++ b/src/hatch/cli/python/show.py @@ -8,9 +8,9 @@ from hatch.cli.application import Application -@click.command(short_help='Show the available Python distributions') -@click.option('--ascii', 'force_ascii', is_flag=True, help='Whether or not to only use ASCII characters') -@click.option('--dir', '-d', 'directory', help='The directory in which distributions reside') +@click.command(short_help="Show the available Python distributions") +@click.option("--ascii", "force_ascii", is_flag=True, help="Whether or not to only use ASCII characters") +@click.option("--dir", "-d", "directory", help="The directory in which distributions reside") @click.pass_obj def show(app: Application, *, force_ascii: bool, directory: str | None): """Show the available Python distributions.""" @@ -19,20 +19,20 @@ def show(app: Application, *, force_ascii: bool, directory: str | None): manager = app.get_python_manager(directory) installed = manager.get_installed() - installed_columns: dict[str, dict[int, str]] = {'Name': {}, 'Version': {}, 'Status': {}} + installed_columns: dict[str, dict[int, str]] = {"Name": {}, "Version": {}, "Status": {}} for i, (name, installed_dist) in enumerate(installed.items()): - installed_columns['Name'][i] = name - installed_columns['Version'][i] = installed_dist.version + installed_columns["Name"][i] = name + installed_columns["Version"][i] = installed_dist.version if installed_dist.needs_update(): - installed_columns['Status'][i] = 'Update available' + installed_columns["Status"][i] = "Update available" - available_columns: dict[str, dict[int, str]] = {'Name': {}, 'Version': {}} + available_columns: dict[str, dict[int, str]] = {"Name": {}, "Version": {}} for i, (name, dist) in enumerate(get_compatible_distributions().items()): if name in installed: continue - available_columns['Name'][i] = name - available_columns['Version'][i] = dist.version.base_version + available_columns["Name"][i] = name + available_columns["Version"][i] = dist.version.base_version - app.display_table('Installed', installed_columns, show_lines=True, force_ascii=force_ascii) - app.display_table('Available', available_columns, show_lines=True, force_ascii=force_ascii) + app.display_table("Installed", installed_columns, show_lines=True, force_ascii=force_ascii) + app.display_table("Available", available_columns, show_lines=True, force_ascii=force_ascii) diff --git a/src/hatch/cli/python/update.py b/src/hatch/cli/python/update.py index ad57e986a..f913f48c0 100644 --- a/src/hatch/cli/python/update.py +++ b/src/hatch/cli/python/update.py @@ -10,9 +10,9 @@ from hatch.cli.application import Application -@click.command(short_help='Update Python distributions') -@click.argument('names', required=True, nargs=-1) -@click.option('--dir', '-d', 'directory', help='The directory in which distributions reside') +@click.command(short_help="Update Python distributions") +@click.argument("names", required=True, nargs=-1) +@click.option("--dir", "-d", "directory", help="The directory in which distributions reside") @click.pass_context def update(ctx: click.Context, *, names: tuple[str, ...], directory: str | None): """ @@ -29,10 +29,10 @@ def update(ctx: click.Context, *, names: tuple[str, ...], directory: str | None) manager = app.get_python_manager(directory) installed = manager.get_installed() - selection = tuple(installed) if 'all' in names else names + selection = tuple(installed) if "all" in names else names not_installed = [name for name in selection if name not in installed] if not_installed: - app.abort(f'Distributions not installed: {", ".join(not_installed)}') + app.abort(f"Distributions not installed: {', '.join(not_installed)}") ctx.invoke(install, names=selection, directory=directory, private=True, update=True) diff --git a/src/hatch/cli/run/__init__.py b/src/hatch/cli/run/__init__.py index db5b13ed6..d105f190f 100644 --- a/src/hatch/cli/run/__init__.py +++ b/src/hatch/cli/run/__init__.py @@ -9,10 +9,10 @@ @click.command( - short_help='Run commands within project environments', - context_settings={'help_option_names': [], 'ignore_unknown_options': True}, + short_help="Run commands within project environments", + context_settings={"help_option_names": [], "ignore_unknown_options": True}, ) -@click.argument('args', metavar='[ENV:]ARGS...', required=True, nargs=-1) +@click.argument("args", metavar="[ENV:]ARGS...", required=True, nargs=-1) @click.pass_context def run(ctx: click.Context, args: tuple[str, ...]): """ @@ -60,13 +60,13 @@ def run(ctx: click.Context, args: tuple[str, ...]): app: Application = ctx.obj first_arg = args[0] - if first_arg in {'-h', '--help'}: + if first_arg in {"-h", "--help"}: app.display_info(ctx.get_help()) return from hatch.utils.fs import Path - if first_arg.endswith('.py') and (script := Path(first_arg)).is_file(): + if first_arg.endswith(".py") and (script := Path(first_arg)).is_file(): from hatch.project.utils import parse_inline_script_metadata # Ensure consistent IDs for storage @@ -75,19 +75,19 @@ def run(ctx: click.Context, args: tuple[str, ...]): try: metadata = parse_inline_script_metadata(script.read_text()) except ValueError as e: - app.abort(f'{e}, {first_arg}') + app.abort(f"{e}, {first_arg}") # Ignore scripts that don't define metadata blocks or define empty metadata blocks if metadata: from hatch.env.utils import ensure_valid_environment - config = metadata.get('tool', {}).get('hatch', {}) - config['skip-install'] = True - config.setdefault('installer', 'uv') - config.setdefault('dependencies', []) - config['dependencies'].extend(metadata.get('dependencies', [])) + config = metadata.get("tool", {}).get("hatch", {}) + config["skip-install"] = True + config.setdefault("installer", "uv") + config.setdefault("dependencies", []) + config["dependencies"].extend(metadata.get("dependencies", [])) - if 'python' not in config and (requires_python := metadata.get('requires-python')) is not None: + if "python" not in config and (requires_python := metadata.get("requires-python")) is not None: import re import sys @@ -95,25 +95,25 @@ def run(ctx: click.Context, args: tuple[str, ...]): from hatch.python.distributions import DISTRIBUTIONS - current_version = '.'.join(map(str, sys.version_info[:2])) - distributions = [name for name in DISTRIBUTIONS if re.match(r'^\d+\.\d+$', name)] + current_version = ".".join(map(str, sys.version_info[:2])) + distributions = [name for name in DISTRIBUTIONS if re.match(r"^\d+\.\d+$", name)] distributions.sort(key=lambda name: name != current_version) python_constraint = SpecifierSet(requires_python) for distribution in distributions: # Try an artificially high patch version to account for # common cases like `>=3.11.4` or `>=3.10,<3.11` - if python_constraint.contains(f'{distribution}.100'): - config['python'] = distribution + if python_constraint.contains(f"{distribution}.100"): + config["python"] = distribution break else: - app.abort(f'Unable to satisfy Python version constraint: {requires_python}') + app.abort(f"Unable to satisfy Python version constraint: {requires_python}") ensure_valid_environment(config) app.project.config.envs[script.id] = config app.project.set_path(script) for context in app.runner_context([script.id]): - context.add_shell_command(['python', first_arg, *args[1:]]) + context.add_shell_command(["python", first_arg, *args[1:]]) return @@ -124,9 +124,9 @@ def run(ctx: click.Context, args: tuple[str, ...]): excluded_variables = [] for i, arg in enumerate(args): command_start = i - if arg.startswith('+'): + if arg.startswith("+"): included_variables.append(arg[1:]) - elif arg.startswith('-'): + elif arg.startswith("-"): excluded_variables.append(arg[1:]) else: break @@ -135,14 +135,14 @@ def run(ctx: click.Context, args: tuple[str, ...]): args = args[command_start:] if not args: - app.abort('Missing argument `MATRIX:ARGS...`') + app.abort("Missing argument `MATRIX:ARGS...`") command, *final_args = args - env_name, separator, command = command.rpartition(':') + env_name, separator, command = command.rpartition(":") if not separator: env_name = app.env elif not env_name: - env_name = 'system' + env_name = "system" ctx.invoke( run_command, diff --git a/src/hatch/cli/self/__init__.py b/src/hatch/cli/self/__init__.py index df0592635..98fedbbe2 100644 --- a/src/hatch/cli/self/__init__.py +++ b/src/hatch/cli/self/__init__.py @@ -4,10 +4,10 @@ from hatch.cli.self.report import report -__management_command = os.environ.get('PYAPP_COMMAND_NAME', 'self') +__management_command = os.environ.get("PYAPP_COMMAND_NAME", "self") -@click.group(name=__management_command, short_help='Manage Hatch') +@click.group(name=__management_command, short_help="Manage Hatch") def self_command(): pass diff --git a/src/hatch/cli/self/report.py b/src/hatch/cli/self/report.py index 44429cb0e..49fe79b45 100644 --- a/src/hatch/cli/self/report.py +++ b/src/hatch/cli/self/report.py @@ -17,54 +17,54 @@ def get_install_source(platform_name: str) -> str: from hatch.utils.fs import Path - default_source = 'pip' + default_source = "pip" python_path = Path(sys.executable).resolve() parent_paths = python_path.parents # https://github.com/ofek/pyapp/blob/v0.13.0/src/app.rs#L27 - if Path(user_data_dir('pyapp', appauthor=False)) in parent_paths: - return 'binary' + if Path(user_data_dir("pyapp", appauthor=False)) in parent_paths: + return "binary" # https://pypa.github.io/pipx/how-pipx-works/ - if (Path.home() / '.local' / 'pipx' / 'venvs') in parent_paths: - return 'pipx' + if (Path.home() / ".local" / "pipx" / "venvs") in parent_paths: + return "pipx" # https://packaging.python.org/en/latest/specifications/externally-managed-environments/#marking-an-interpreter-as-using-an-external-package-manager try: - stdlib_path_config = sysconfig.get_path('stdlib') + stdlib_path_config = sysconfig.get_path("stdlib") # https://docs.python.org/3/library/sysconfig.html#sysconfig.get_path except KeyError: - stdlib_path_config = '' + stdlib_path_config = "" if ( # This does not work on NixOS, see: https://github.com/NixOS/nixpkgs/issues/201037 sys.prefix == sys.base_prefix and stdlib_path_config and (stdlib_path := Path(stdlib_path_config)).is_dir() - and any(p.name == 'EXTERNALLY-MANAGED' and p.is_file() for p in stdlib_path.iterdir()) + and any(p.name == "EXTERNALLY-MANAGED" and p.is_file() for p in stdlib_path.iterdir()) ): - return 'system' + return "system" - if platform_name == 'windows': - if sys.executable.endswith('WindowsApps\\python.exe'): - return 'Windows Store' + if platform_name == "windows": + if sys.executable.endswith("WindowsApps\\python.exe"): + return "Windows Store" # Break early because nothing after is applicable return default_source - if platform_name == 'macos' and Path('/usr/local/Cellar') in parent_paths: # no cov - return 'Homebrew' + if platform_name == "macos" and Path("/usr/local/Cellar") in parent_paths: # no cov + return "Homebrew" # https://github.com/pyenv/pyenv/tree/v2.3.35#set-up-your-shell-environment-for-pyenv - if Path(os.environ.get('PYENV_ROOT', '~/.pyenv')).expand() in parent_paths: - return 'Pyenv' + if Path(os.environ.get("PYENV_ROOT", "~/.pyenv")).expand() in parent_paths: + return "Pyenv" return default_source -@click.command(short_help='Generate a pre-populated GitHub issue') -@click.option('--no-open', '-n', is_flag=True, help='Show the URL instead of opening it') +@click.command(short_help="Generate a pre-populated GitHub issue") +@click.option("--no-open", "-n", is_flag=True, help="Show the URL instead of opening it") @click.pass_obj def report(app: Application, *, no_open: bool) -> None: """Generate a pre-populated GitHub issue.""" @@ -81,17 +81,17 @@ def report(app: Application, *, no_open: bool) -> None: # Retain the config that would be most useful full_config = load_toml_data(app.config_file.read_scrubbed()) relevant_config = {} - for setting in ('mode', 'shell'): + for setting in ("mode", "shell"): if setting in full_config: relevant_config[setting] = full_config[setting] - if env_dirs := relevant_config.get('dirs', {}).get('envs'): - relevant_config['dirs'] = {'envs': env_dirs} + if env_dirs := relevant_config.get("dirs", {}).get("envs"): + relevant_config["dirs"] = {"envs": env_dirs} # Try to determine how Hatch was installed source = get_install_source(app.platform.name) - element_padding = ' ' * 4 + element_padding = " " * 4 body = f"""\ ## Current behavior @@ -121,7 +121,7 @@ def report(app: Application, *, no_open: bool) -> None: ``` """ - url = f'https://github.com/pypa/hatch/issues/new?body={quote_plus(body)}' + url = f"https://github.com/pypa/hatch/issues/new?body={quote_plus(body)}" if no_open: app.display(url) else: diff --git a/src/hatch/cli/self/restore.py b/src/hatch/cli/self/restore.py index 77f2ce67e..df18e635e 100644 --- a/src/hatch/cli/self/restore.py +++ b/src/hatch/cli/self/restore.py @@ -9,9 +9,9 @@ @click.command( - short_help='Restore the installation', context_settings={'help_option_names': [], 'ignore_unknown_options': True} + short_help="Restore the installation", context_settings={"help_option_names": [], "ignore_unknown_options": True} ) -@click.argument('args', nargs=-1) +@click.argument("args", nargs=-1) @click.pass_obj def restore(app: Application, *, args: tuple[str, ...]): # noqa: ARG001 - app.abort('Hatch is not installed as a binary') + app.abort("Hatch is not installed as a binary") diff --git a/src/hatch/cli/self/update.py b/src/hatch/cli/self/update.py index e532288d3..50c0e69d1 100644 --- a/src/hatch/cli/self/update.py +++ b/src/hatch/cli/self/update.py @@ -9,9 +9,9 @@ @click.command( - short_help='Install the latest version', context_settings={'help_option_names': [], 'ignore_unknown_options': True} + short_help="Install the latest version", context_settings={"help_option_names": [], "ignore_unknown_options": True} ) -@click.argument('args', nargs=-1) +@click.argument("args", nargs=-1) @click.pass_obj def update(app: Application, *, args: tuple[str, ...]): # noqa: ARG001 - app.abort('Hatch is not installed as a binary') + app.abort("Hatch is not installed as a binary") diff --git a/src/hatch/cli/shell/__init__.py b/src/hatch/cli/shell/__init__.py index 182258a79..815e71d98 100644 --- a/src/hatch/cli/shell/__init__.py +++ b/src/hatch/cli/shell/__init__.py @@ -9,9 +9,9 @@ @click.command(short_help="Enter a shell within a project's environment") -@click.argument('env_name', required=False) -@click.option('--name') -@click.option('--path') +@click.argument("env_name", required=False) +@click.option("--name") +@click.option("--path") @click.pass_obj def shell(app: Application, env_name: str | None, name: str, path: str): # no cov """Enter a shell within a project's environment.""" @@ -19,12 +19,12 @@ def shell(app: Application, env_name: str | None, name: str, path: str): # no c chosen_env = env_name or app.env if chosen_env == app.env_active: - app.abort(f'Already in environment: {chosen_env}') + app.abort(f"Already in environment: {chosen_env}") for matrices in (app.project.config.matrices, app.project.config.internal_matrices): if chosen_env in matrices: - app.display_error(f'Environment `{chosen_env}` defines a matrix, choose one of the following instead:\n') - for generated_name in matrices[chosen_env]['envs']: + app.display_error(f"Environment `{chosen_env}` defines a matrix, choose one of the following instead:\n") + for generated_name in matrices[chosen_env]["envs"]: app.display_error(generated_name) app.abort() @@ -44,11 +44,11 @@ def shell(app: Application, env_name: str | None, name: str, path: str): # no c environment = app.project.get_environment(chosen_env) app.project.prepare_environment(environment) - first_run_indicator = app.cache_dir / 'shell' / 'first_run' + first_run_indicator = app.cache_dir / "shell" / "first_run" if not first_run_indicator.is_file(): app.display_waiting( - 'You are about to enter a new shell, exit as you usually would e.g. ' - 'by typing `exit` or pressing `ctrl+d`...' + "You are about to enter a new shell, exit as you usually would e.g. " + "by typing `exit` or pressing `ctrl+d`..." ) first_run_indicator.parent.ensure_dir_exists() first_run_indicator.touch() diff --git a/src/hatch/cli/status/__init__.py b/src/hatch/cli/status/__init__.py index caa898751..63b0282a6 100644 --- a/src/hatch/cli/status/__init__.py +++ b/src/hatch/cli/status/__init__.py @@ -1,27 +1,27 @@ import click -@click.command(short_help='Show information about the current environment') +@click.command(short_help="Show information about the current environment") @click.pass_obj def status(app): """Show information about the current environment.""" def display_pair(key, value, display_func=None, link=None): - app.display_info('[', end='') - app.display_success(key, end='') - app.display_info(']', end='') - app.display_info(' - ', end='') + app.display_info("[", end="") + app.display_success(key, end="") + app.display_info("]", end="") + app.display_info(" - ", end="") (display_func or app.display_info)(value, link=link) if app.project.root is None: if app.project.chosen_name is None: - display_pair('Project', '', app.display_warning) + display_pair("Project", "", app.display_warning) else: - display_pair('Project', f'{app.project.chosen_name} (not a project)', app.display_warning) + display_pair("Project", f"{app.project.chosen_name} (not a project)", app.display_warning) elif app.project.chosen_name is None: - display_pair('Project', f'{app.project.root.name} (current directory)') + display_pair("Project", f"{app.project.root.name} (current directory)") else: - display_pair('Project', app.project.chosen_name) + display_pair("Project", app.project.chosen_name) - display_pair('Location', str(app.project.location)) - display_pair('Config', str(app.config_file.path), link=f'file:///{app.config_file.path}') + display_pair("Location", str(app.project.location)) + display_pair("Config", str(app.config_file.path), link=f"file:///{app.config_file.path}") diff --git a/src/hatch/cli/terminal.py b/src/hatch/cli/terminal.py index ca79bf755..75cb0ac47 100644 --- a/src/hatch/cli/terminal.py +++ b/src/hatch/cli/terminal.py @@ -72,11 +72,11 @@ def stop(self) -> None: if self.__verbosity > 0 and active: if not final_text: final_text = old_message.plain - final_text = f'Finished {final_text[:1].lower()}{final_text[1:]}' + final_text = f"Finished {final_text[:1].lower()}{final_text[1:]}" self.__output(Text(final_text, style=self.__success_style)) - def __call__(self, message: str, final_text: str = '') -> BorrowedStatus: + def __call__(self, message: str, final_text: str = "") -> BorrowedStatus: self.__messages.append((Text(message, style=self.__waiting_style), final_text)) return self @@ -104,7 +104,7 @@ def __exit__(self, exc_type, exc_val, exc_tb): if self.__verbosity > 0 and self.__active(): if not final_text: final_text = old_message.plain - final_text = f'Finished {final_text[:1].lower()}{final_text[1:]}' + final_text = f"Finished {final_text[:1].lower()}{final_text[1:]}" self.__output(Text(final_text, style=self.__success_style)) @@ -127,7 +127,7 @@ def __active(self) -> bool: def __output(self, text): self.__console.stderr = True try: - self.__console.print(text, overflow='ignore', no_wrap=True, crop=False) + self.__console.print(text, overflow="ignore", no_wrap=True, crop=False) finally: self.__console.stderr = False @@ -135,7 +135,7 @@ def __output(self, text): class Terminal: def __init__(self, *, verbosity: int, enable_color: bool | None, interactive: bool | None): # Force consistent output for test assertions - self.testing = 'HATCH_SELF_TESTING' in os.environ + self.testing = "HATCH_SELF_TESTING" in os.environ self.verbosity = verbosity self.console = Console( @@ -149,20 +149,20 @@ def __init__(self, *, verbosity: int, enable_color: bool | None, interactive: bo ) # Set defaults so we can pretty print before loading user config - self._style_level_success: Style | str = 'bold cyan' - self._style_level_error: Style | str = 'bold red' - self._style_level_warning: Style | str = 'bold yellow' - self._style_level_waiting: Style | str = 'bold magenta' + self._style_level_success: Style | str = "bold cyan" + self._style_level_error: Style | str = "bold red" + self._style_level_warning: Style | str = "bold yellow" + self._style_level_waiting: Style | str = "bold magenta" # Default is simply bold rather than bold white for shells that have been configured with a white background - self._style_level_info: Style | str = 'bold' - self._style_level_debug: Style | str = 'bold' + self._style_level_info: Style | str = "bold" + self._style_level_debug: Style | str = "bold" # Chosen as the default since it's compatible everywhere and looks nice - self._style_spinner = 'simpleDotsScrolling' + self._style_spinner = "simpleDotsScrolling" @cached_property def kv_separator(self) -> Text: - return self.style_warning('->') + return self.style_warning("->") def style_success(self, text: str) -> Text: return Text(text, style=self._style_level_success) @@ -190,76 +190,76 @@ def initialize_styles(self, styles: dict): # no cov errors = [] for option, style in styles.items(): - attribute = f'_style_level_{option}' + attribute = f"_style_level_{option}" default_level = getattr(self, attribute, None) if default_level: try: parsed_style = Style.parse(style) except StyleSyntaxError as e: # no cov - errors.append(f'Invalid style definition for `{option}`, defaulting to `{default_level}`: {e}') + errors.append(f"Invalid style definition for `{option}`, defaulting to `{default_level}`: {e}") parsed_style = Style.parse(default_level) setattr(self, attribute, parsed_style) - elif option == 'spinner': + elif option == "spinner": try: Spinner(style) except KeyError as e: errors.append( - f'Invalid style definition for `{option}`, defaulting to `{self._style_spinner}`: {e.args[0]}' + f"Invalid style definition for `{option}`, defaulting to `{self._style_spinner}`: {e.args[0]}" ) else: self._style_spinner = style else: - setattr(self, f'_style_{option}', style) + setattr(self, f"_style_{option}", style) return errors - def display(self, text='', **kwargs): - self.console.print(text, style=self._style_level_info, overflow='ignore', no_wrap=True, crop=False, **kwargs) + def display(self, text="", **kwargs): + self.console.print(text, style=self._style_level_info, overflow="ignore", no_wrap=True, crop=False, **kwargs) - def display_critical(self, text='', **kwargs): + def display_critical(self, text="", **kwargs): self.console.stderr = True try: self.console.print( - text, style=self._style_level_error, overflow='ignore', no_wrap=True, crop=False, **kwargs + text, style=self._style_level_error, overflow="ignore", no_wrap=True, crop=False, **kwargs ) finally: self.console.stderr = False - def display_error(self, text='', *, stderr=True, indent=None, link=None, **kwargs): + def display_error(self, text="", *, stderr=True, indent=None, link=None, **kwargs): if self.verbosity < -2: # noqa: PLR2004 return self._output(text, self._style_level_error, stderr=stderr, indent=indent, link=link, **kwargs) - def display_warning(self, text='', *, stderr=True, indent=None, link=None, **kwargs): + def display_warning(self, text="", *, stderr=True, indent=None, link=None, **kwargs): if self.verbosity < -1: return self._output(text, self._style_level_warning, stderr=stderr, indent=indent, link=link, **kwargs) - def display_info(self, text='', *, stderr=True, indent=None, link=None, **kwargs): + def display_info(self, text="", *, stderr=True, indent=None, link=None, **kwargs): if self.verbosity < 0: return self._output(text, self._style_level_info, stderr=stderr, indent=indent, link=link, **kwargs) - def display_success(self, text='', *, stderr=True, indent=None, link=None, **kwargs): + def display_success(self, text="", *, stderr=True, indent=None, link=None, **kwargs): if self.verbosity < 0: return self._output(text, self._style_level_success, stderr=stderr, indent=indent, link=link, **kwargs) - def display_waiting(self, text='', *, stderr=True, indent=None, link=None, **kwargs): + def display_waiting(self, text="", *, stderr=True, indent=None, link=None, **kwargs): if self.verbosity < 0: return self._output(text, self._style_level_waiting, stderr=stderr, indent=indent, link=link, **kwargs) - def display_debug(self, text='', level=1, *, stderr=True, indent=None, link=None, **kwargs): + def display_debug(self, text="", level=1, *, stderr=True, indent=None, link=None, **kwargs): if not 1 <= level <= 3: # noqa: PLR2004 - error_message = 'Debug output can only have verbosity levels between 1 and 3 (inclusive)' + error_message = "Debug output can only have verbosity levels between 1 and 3 (inclusive)" raise ValueError(error_message) if self.verbosity < level: @@ -271,17 +271,17 @@ def display_mini_header(self, text, *, stderr=False, indent=None, link=None): if self.verbosity < 0: return - self.display_info('[', stderr=stderr, indent=indent, end='') - self.display_success(text, stderr=stderr, link=link, end='') - self.display_info(']', stderr=stderr) + self.display_info("[", stderr=stderr, indent=indent, end="") + self.display_success(text, stderr=stderr, link=link, end="") + self.display_info("]", stderr=stderr) - def display_header(self, title=''): + def display_header(self, title=""): self.console.rule(Text(title, self._style_level_success)) def display_syntax(self, *args, **kwargs): from rich.syntax import Syntax - kwargs.setdefault('background_color', 'default' if self.testing else None) + kwargs.setdefault("background_color", "default" if self.testing else None) self.output(Syntax(*args, **kwargs)) def display_markdown(self, text, **kwargs): # no cov @@ -302,15 +302,15 @@ def display_table(self, title, columns, *, show_lines=False, column_options=None if force_ascii: from rich.box import ASCII_DOUBLE_HEAD - table_options['box'] = ASCII_DOUBLE_HEAD - table_options['safe_box'] = True + table_options["box"] = ASCII_DOUBLE_HEAD + table_options["safe_box"] = True - table = Table(title=title, show_lines=show_lines, title_style='', **table_options) + table = Table(title=title, show_lines=show_lines, title_style="", **table_options) columns = dict(columns) for column_title, indices in list(columns.items()): if indices: - table.add_column(column_title, style='bold', **column_options.get(column_title, {})) + table.add_column(column_title, style="bold", **column_options.get(column_title, {})) else: columns.pop(column_title) @@ -318,7 +318,7 @@ def display_table(self, title, columns, *, show_lines=False, column_options=None return for i in range(num_rows or max(map(max, columns.values())) + 1): - row = [indices.get(i, '') for indices in columns.values()] + row = [indices.get(i, "") for indices in columns.values()] if any(row): table.add_row(*row) @@ -333,14 +333,14 @@ def status(self) -> BorrowedStatus: spinner_style=self._style_spinner, waiting_style=self._style_level_waiting, success_style=self._style_level_success, - initializer=lambda: setattr(self.platform, 'displaying_status', True), # type: ignore[attr-defined] - finalizer=lambda: setattr(self.platform, 'displaying_status', False), # type: ignore[attr-defined] + initializer=lambda: setattr(self.platform, "displaying_status", True), # type: ignore[attr-defined] + finalizer=lambda: setattr(self.platform, "displaying_status", False), # type: ignore[attr-defined] ) def status_if(self, *args, condition: bool, **kwargs) -> TerminalStatus: return self.status(*args, **kwargs) if condition else NullStatus() - def _output(self, text='', style=None, *, stderr=False, indent=None, link=None, **kwargs): + def _output(self, text="", style=None, *, stderr=False, indent=None, link=None, **kwargs): if indent: text = indent_text(text, indent) @@ -350,9 +350,9 @@ def _output(self, text='', style=None, *, stderr=False, indent=None, link=None, self.output(text, stderr=stderr, style=style, **kwargs) def output(self, *args, stderr=False, **kwargs): - kwargs.setdefault('overflow', 'ignore') - kwargs.setdefault('no_wrap', True) - kwargs.setdefault('crop', False) + kwargs.setdefault("overflow", "ignore") + kwargs.setdefault("no_wrap", True) + kwargs.setdefault("crop", False) if not stderr: self.console.print(*args, **kwargs) diff --git a/src/hatch/cli/test/__init__.py b/src/hatch/cli/test/__init__.py index 48640636f..343a5a3a4 100644 --- a/src/hatch/cli/test/__init__.py +++ b/src/hatch/cli/test/__init__.py @@ -9,19 +9,19 @@ from hatch.env.plugin.interface import EnvironmentInterface -@click.command(short_help='Run tests', context_settings={'ignore_unknown_options': True}) -@click.argument('args', nargs=-1) -@click.option('--randomize', '-r', is_flag=True, help='Randomize the order of test execution') -@click.option('--parallel', '-p', is_flag=True, help='Parallelize test execution') -@click.option('--retries', type=int, help='Number of times to retry failed tests') -@click.option('--retry-delay', type=float, help='Seconds to wait between retries') -@click.option('--cover', '-c', is_flag=True, help='Measure code coverage') -@click.option('--cover-quiet', is_flag=True, help='Disable coverage reporting after tests, implicitly enabling --cover') -@click.option('--all', '-a', 'test_all', is_flag=True, help='Test all environments in the matrix') -@click.option('--python', '-py', help='The Python versions to test, equivalent to: -i py=...') -@click.option('--include', '-i', 'included_variable_specs', multiple=True, help='The matrix variables to include') -@click.option('--exclude', '-x', 'excluded_variable_specs', multiple=True, help='The matrix variables to exclude') -@click.option('--show', '-s', is_flag=True, help='Show information about environments in the matrix') +@click.command(short_help="Run tests", context_settings={"ignore_unknown_options": True}) +@click.argument("args", nargs=-1) +@click.option("--randomize", "-r", is_flag=True, help="Randomize the order of test execution") +@click.option("--parallel", "-p", is_flag=True, help="Parallelize test execution") +@click.option("--retries", type=int, help="Number of times to retry failed tests") +@click.option("--retry-delay", type=float, help="Seconds to wait between retries") +@click.option("--cover", "-c", is_flag=True, help="Measure code coverage") +@click.option("--cover-quiet", is_flag=True, help="Disable coverage reporting after tests, implicitly enabling --cover") +@click.option("--all", "-a", "test_all", is_flag=True, help="Test all environments in the matrix") +@click.option("--python", "-py", help="The Python versions to test, equivalent to: -i py=...") +@click.option("--include", "-i", "included_variable_specs", multiple=True, help="The matrix variables to include") +@click.option("--exclude", "-x", "excluded_variable_specs", multiple=True, help="The matrix variables to exclude") +@click.option("--show", "-s", is_flag=True, help="Show information about environments in the matrix") @click.pass_context def test( ctx: click.Context, @@ -83,8 +83,8 @@ def test( show_env, internal=True, hide_titles=True, - force_ascii=os.environ.get('HATCH_SELF_TESTING') == 'true', - envs=ctx.obj.project.config.internal_matrices['hatch-test']['envs'] if app.verbose else ['hatch-test'], + force_ascii=os.environ.get("HATCH_SELF_TESTING") == "true", + envs=ctx.obj.project.config.internal_matrices["hatch-test"]["envs"] if app.verbose else ["hatch-test"], ) return @@ -97,27 +97,27 @@ def test( from hatch.utils.runner import parse_matrix_variables, select_environments if python is not None: - included_variable_specs = (f'py={python}', *included_variable_specs) + included_variable_specs = (f"py={python}", *included_variable_specs) try: included_variables = parse_matrix_variables(included_variable_specs) except ValueError as e: - app.abort(f'Duplicate included variable: {e}') + app.abort(f"Duplicate included variable: {e}") try: excluded_variables = parse_matrix_variables(excluded_variable_specs) except ValueError as e: - app.abort(f'Duplicate excluded variable: {e}') + app.abort(f"Duplicate excluded variable: {e}") if test_all and (included_variables or excluded_variables): - app.abort('The --all option cannot be used with the --include or --exclude options.') + app.abort("The --all option cannot be used with the --include or --exclude options.") if retries is None and retry_delay is not None: - app.abort('The --retry-delay option requires the --retries option to be set as well.') + app.abort("The --retry-delay option requires the --retries option to be set as well.") app.ensure_environment_plugin_dependencies() - test_envs = app.project.config.internal_matrices['hatch-test']['envs'] + test_envs = app.project.config.internal_matrices["hatch-test"]["envs"] selected_envs: list[str] = [] multiple_possible = True if test_all: @@ -128,9 +128,9 @@ def test( multiple_possible = False # Prioritize candidates that seems to match the running interpreter - current_version = '.'.join(map(str, sys.version_info[:2])) + current_version = ".".join(map(str, sys.version_info[:2])) candidate_names: list[str] = list(test_envs) - candidate_names.sort(key=lambda name: test_envs[name].get('python') != current_version) + candidate_names.sort(key=lambda name: test_envs[name].get("python") != current_version) candidate_envs: list[EnvironmentInterface] = [] for candidate_name in candidate_names: @@ -152,53 +152,53 @@ def test( selected_envs.append(candidate_env.name) break else: - app.abort(f'No compatible environments found: {candidate_envs}') + app.abort(f"No compatible environments found: {candidate_envs}") - test_script = 'run-cov' if cover else 'run' - patched_coverage = PatchedCoverageConfig(app.project.location, app.data_dir / '.config') + test_script = "run-cov" if cover else "run" + patched_coverage = PatchedCoverageConfig(app.project.location, app.data_dir / ".config") coverage_config_file = str(patched_coverage.internal_config_path) if cover: patched_coverage.write_config_file() for context in app.runner_context(selected_envs, ignore_compat=multiple_possible, display_header=multiple_possible): - internal_arguments: list[str] = list(context.env.config.get('extra-args', [])) + internal_arguments: list[str] = list(context.env.config.get("extra-args", [])) - if not context.env.config.get('randomize', randomize): - internal_arguments.extend(['-p', 'no:randomly']) + if not context.env.config.get("randomize", randomize): + internal_arguments.extend(["-p", "no:randomly"]) - if context.env.config.get('parallel', parallel): - internal_arguments.extend(['-n', 'logical']) + if context.env.config.get("parallel", parallel): + internal_arguments.extend(["-n", "logical"]) - if (num_retries := context.env.config.get('retries', retries)) is not None: - if '-r' not in args: - internal_arguments.extend(['-r', 'aR']) + if (num_retries := context.env.config.get("retries", retries)) is not None: + if "-r" not in args: + internal_arguments.extend(["-r", "aR"]) - internal_arguments.extend(['--reruns', str(num_retries)]) + internal_arguments.extend(["--reruns", str(num_retries)]) - if (seconds_delay := context.env.config.get('retry-delay', retry_delay)) is not None: - internal_arguments.extend(['--reruns-delay', str(seconds_delay)]) + if (seconds_delay := context.env.config.get("retry-delay", retry_delay)) is not None: + internal_arguments.extend(["--reruns-delay", str(seconds_delay)]) internal_args = context.env.join_command_args(internal_arguments) if internal_args: # Add an extra space if required - internal_args = f' {internal_args}' + internal_args = f" {internal_args}" arguments: list[str] = [] if args: arguments.extend(args) else: - arguments.extend(context.env.config.get('default-args', ['tests'])) + arguments.extend(context.env.config.get("default-args", ["tests"])) context.add_shell_command([test_script, *arguments]) - context.env_vars['HATCH_TEST_ARGS'] = internal_args + context.env_vars["HATCH_TEST_ARGS"] = internal_args if cover: - context.env_vars['COVERAGE_RCFILE'] = coverage_config_file - context.env_vars['COVERAGE_PROCESS_START'] = coverage_config_file + context.env_vars["COVERAGE_RCFILE"] = coverage_config_file + context.env_vars["COVERAGE_PROCESS_START"] = coverage_config_file if cover: for context in app.runner_context([selected_envs[0]]): - context.add_shell_command('cov-combine') + context.add_shell_command("cov-combine") if not cover_quiet: for context in app.runner_context([selected_envs[0]]): - context.add_shell_command('cov-report') + context.add_shell_command("cov-report") diff --git a/src/hatch/cli/test/core.py b/src/hatch/cli/test/core.py index 1de06965c..a9215b886 100644 --- a/src/hatch/cli/test/core.py +++ b/src/hatch/cli/test/core.py @@ -19,28 +19,28 @@ def user_config_path(self) -> Path: # https://coverage.readthedocs.io/en/7.4.4/config.html#sample-file return ( dedicated_coverage_file - if (dedicated_coverage_file := self.project_root.joinpath('.coveragerc')).is_file() - else self.project_root.joinpath('pyproject.toml') + if (dedicated_coverage_file := self.project_root.joinpath(".coveragerc")).is_file() + else self.project_root.joinpath("pyproject.toml") ) @cached_property def internal_config_path(self) -> Path: - return self.data_dir / 'coverage' / self.project_root.id / self.user_config_path.name + return self.data_dir / "coverage" / self.project_root.id / self.user_config_path.name def write_config_file(self) -> None: self.internal_config_path.parent.ensure_dir_exists() - if self.internal_config_path.name == '.coveragerc': + if self.internal_config_path.name == ".coveragerc": from configparser import ConfigParser cfg = ConfigParser() cfg.read(str(self.user_config_path)) - if 'run' not in cfg: - cfg['run'] = {'parallel': 'true'} + if "run" not in cfg: + cfg["run"] = {"parallel": "true"} self._write_ini(cfg) return - cfg['run']['parallel'] = 'true' + cfg["run"]["parallel"] = "true" self._write_ini(cfg) else: import tomli_w @@ -48,9 +48,9 @@ def write_config_file(self) -> None: from hatch.utils.toml import load_toml_data project_data = load_toml_data(self.user_config_path.read_text()) - project_data.setdefault('tool', {}).setdefault('coverage', {}).setdefault('run', {})['parallel'] = True + project_data.setdefault("tool", {}).setdefault("coverage", {}).setdefault("run", {})["parallel"] = True self.internal_config_path.write_text(tomli_w.dumps(project_data)) def _write_ini(self, cfg: ConfigParser) -> None: - with self.internal_config_path.open('w', encoding='utf-8') as f: + with self.internal_config_path.open("w", encoding="utf-8") as f: cfg.write(f) diff --git a/src/hatch/cli/version/__init__.py b/src/hatch/cli/version/__init__.py index b89bfc3ec..0c329bc32 100644 --- a/src/hatch/cli/version/__init__.py +++ b/src/hatch/cli/version/__init__.py @@ -9,27 +9,27 @@ @click.command(short_help="View or set a project's version") -@click.argument('desired_version', required=False) +@click.argument("desired_version", required=False) @click.option( - '--force', - '-f', + "--force", + "-f", is_flag=True, - help='Allow an explicit downgrading version to be given', + help="Allow an explicit downgrading version to be given", ) @click.pass_obj def version(app: Application, *, desired_version: str | None, force: bool): """View or set a project's version.""" if app.project.root is None: if app.project.chosen_name is None: - app.abort('No project detected') + app.abort("No project detected") else: - app.abort(f'Project {app.project.chosen_name} (not a project)') + app.abort(f"Project {app.project.chosen_name} (not a project)") - if 'version' in app.project.metadata.config.get('project', {}): + if "version" in app.project.metadata.config.get("project", {}): if desired_version: - app.abort('Cannot set version when it is statically defined by the `project.version` field') + app.abort("Cannot set version when it is statically defined by the `project.version` field") else: - app.display(app.project.metadata.config['project']['version']) + app.display(app.project.metadata.config["project"]["version"]) return from hatch.config.constants import VersionEnvVars @@ -38,7 +38,7 @@ def version(app: Application, *, desired_version: str | None, force: bool): with app.project.location.as_cwd(): if app.project.metadata.build.build_backend != BUILD_BACKEND: if desired_version: - app.abort('The version can only be set when Hatchling is the build backend') + app.abort("The version can only be set when Hatchling is the build backend") app.ensure_environment_plugin_dependencies() app.project.prepare_build_environment() @@ -46,7 +46,7 @@ def version(app: Application, *, desired_version: str | None, force: bool): with app.project.location.as_cwd(), app.project.build_env.get_env_vars(): project_metadata = app.project.build_frontend.get_core_metadata() - app.display(project_metadata['version']) + app.display(project_metadata["version"]) else: from hatch.utils.runner import ExecutionContext @@ -54,11 +54,11 @@ def version(app: Application, *, desired_version: str | None, force: bool): app.project.prepare_build_environment() context = ExecutionContext(app.project.build_env) - command = ['python', '-u', '-m', 'hatchling', 'version'] + command = ["python", "-u", "-m", "hatchling", "version"] if desired_version: command.append(desired_version) if force: - context.env_vars[VersionEnvVars.VALIDATE_BUMP] = 'false' + context.env_vars[VersionEnvVars.VALIDATE_BUMP] = "false" context.add_shell_command(command) app.execute_context(context) diff --git a/src/hatch/config/constants.py b/src/hatch/config/constants.py index 2f2f73ee3..6dd2097a8 100644 --- a/src/hatch/config/constants.py +++ b/src/hatch/config/constants.py @@ -1,39 +1,39 @@ class AppEnvVars: - ENV = 'HATCH_ENV' - ENV_ACTIVE = 'HATCH_ENV_ACTIVE' - ENV_OPTION_PREFIX = 'HATCH_ENV_TYPE_' - QUIET = 'HATCH_QUIET' - VERBOSE = 'HATCH_VERBOSE' - INTERACTIVE = 'HATCH_INTERACTIVE' - PYTHON = 'HATCH_PYTHON' + ENV = "HATCH_ENV" + ENV_ACTIVE = "HATCH_ENV_ACTIVE" + ENV_OPTION_PREFIX = "HATCH_ENV_TYPE_" + QUIET = "HATCH_QUIET" + VERBOSE = "HATCH_VERBOSE" + INTERACTIVE = "HATCH_INTERACTIVE" + PYTHON = "HATCH_PYTHON" # https://no-color.org - NO_COLOR = 'NO_COLOR' - FORCE_COLOR = 'FORCE_COLOR' + NO_COLOR = "NO_COLOR" + FORCE_COLOR = "FORCE_COLOR" class ConfigEnvVars: - PROJECT = 'HATCH_PROJECT' - DATA = 'HATCH_DATA_DIR' - CACHE = 'HATCH_CACHE_DIR' - CONFIG = 'HATCH_CONFIG' + PROJECT = "HATCH_PROJECT" + DATA = "HATCH_DATA_DIR" + CACHE = "HATCH_CACHE_DIR" + CONFIG = "HATCH_CONFIG" class PublishEnvVars: - USER = 'HATCH_INDEX_USER' - AUTH = 'HATCH_INDEX_AUTH' - REPO = 'HATCH_INDEX_REPO' - CA_CERT = 'HATCH_INDEX_CA_CERT' - CLIENT_CERT = 'HATCH_INDEX_CLIENT_CERT' - CLIENT_KEY = 'HATCH_INDEX_CLIENT_KEY' - PUBLISHER = 'HATCH_PUBLISHER' - OPTIONS = 'HATCH_PUBLISHER_OPTIONS' + USER = "HATCH_INDEX_USER" + AUTH = "HATCH_INDEX_AUTH" + REPO = "HATCH_INDEX_REPO" + CA_CERT = "HATCH_INDEX_CA_CERT" + CLIENT_CERT = "HATCH_INDEX_CLIENT_CERT" + CLIENT_KEY = "HATCH_INDEX_CLIENT_KEY" + PUBLISHER = "HATCH_PUBLISHER" + OPTIONS = "HATCH_PUBLISHER_OPTIONS" class PythonEnvVars: - CUSTOM_SOURCE_PREFIX = 'HATCH_PYTHON_CUSTOM_SOURCE_' - CUSTOM_PATH_PREFIX = 'HATCH_PYTHON_CUSTOM_PATH_' - CUSTOM_VERSION_PREFIX = 'HATCH_PYTHON_CUSTOM_VERSION_' + CUSTOM_SOURCE_PREFIX = "HATCH_PYTHON_CUSTOM_SOURCE_" + CUSTOM_PATH_PREFIX = "HATCH_PYTHON_CUSTOM_PATH_" + CUSTOM_VERSION_PREFIX = "HATCH_PYTHON_CUSTOM_VERSION_" class VersionEnvVars: - VALIDATE_BUMP = 'HATCH_VERSION_VALIDATE_BUMP' + VALIDATE_BUMP = "HATCH_VERSION_VALIDATE_BUMP" diff --git a/src/hatch/config/model.py b/src/hatch/config/model.py index e4698ac04..1de3d4177 100644 --- a/src/hatch/config/model.py +++ b/src/hatch/config/model.py @@ -9,7 +9,7 @@ def __init__(self, *args, location): super().__init__(*args) def __str__(self): - return f'Error parsing config:\n{self.location}\n {super().__str__()}' + return f"Error parsing config:\n{self.location}\n {super().__str__()}" def parse_config(obj): @@ -30,7 +30,7 @@ def __init__(self, config: dict, steps: tuple = ()): def parse_fields(self): for attribute in self.__dict__: - _, prefix, name = attribute.partition('_field_') + _, prefix, name = attribute.partition("_field_") if prefix: parse_config(getattr(self, name)) @@ -38,7 +38,7 @@ def raise_error(self, message, *, extra_steps=()): import inspect field = inspect.currentframe().f_back.f_code.co_name - raise ConfigurationError(message, location=' -> '.join([*self.steps, field, *extra_steps])) + raise ConfigurationError(message, location=" -> ".join([*self.steps, field, *extra_steps])) class RootConfig(LazilyParsedConfig): @@ -57,179 +57,179 @@ def __init__(self, *args, **kwargs): @property def mode(self): if self._field_mode is FIELD_TO_PARSE: - if 'mode' in self.raw_data: - mode = self.raw_data['mode'] + if "mode" in self.raw_data: + mode = self.raw_data["mode"] if not isinstance(mode, str): - self.raise_error('must be a string') + self.raise_error("must be a string") - valid_modes = ('aware', 'local', 'project') + valid_modes = ("aware", "local", "project") if mode not in valid_modes: - self.raise_error(f'must be one of: {", ".join(valid_modes)}') + self.raise_error(f"must be one of: {', '.join(valid_modes)}") self._field_mode = mode else: - self._field_mode = self.raw_data['mode'] = 'local' + self._field_mode = self.raw_data["mode"] = "local" return self._field_mode @mode.setter def mode(self, value): - self.raw_data['mode'] = value + self.raw_data["mode"] = value self._field_mode = FIELD_TO_PARSE @property def project(self): if self._field_project is FIELD_TO_PARSE: - if 'project' in self.raw_data: - project = self.raw_data['project'] + if "project" in self.raw_data: + project = self.raw_data["project"] if not isinstance(project, str): - self.raise_error('must be a string') + self.raise_error("must be a string") self._field_project = project else: - self._field_project = self.raw_data['project'] = '' + self._field_project = self.raw_data["project"] = "" return self._field_project @project.setter def project(self, value): - self.raw_data['project'] = value + self.raw_data["project"] = value self._field_project = FIELD_TO_PARSE @property def shell(self): if self._field_shell is FIELD_TO_PARSE: - if 'shell' in self.raw_data: - shell = self.raw_data['shell'] + if "shell" in self.raw_data: + shell = self.raw_data["shell"] if isinstance(shell, str): - self._field_shell = ShellConfig({'name': shell}, ('shell',)) + self._field_shell = ShellConfig({"name": shell}, ("shell",)) elif isinstance(shell, dict): - self._field_shell = ShellConfig(shell, ('shell',)) + self._field_shell = ShellConfig(shell, ("shell",)) else: - self.raise_error('must be a string or table') + self.raise_error("must be a string or table") else: - self.raw_data['shell'] = '' - self._field_shell = ShellConfig({'name': ''}, ('shell',)) + self.raw_data["shell"] = "" + self._field_shell = ShellConfig({"name": ""}, ("shell",)) return self._field_shell @shell.setter def shell(self, value): - self.raw_data['shell'] = value + self.raw_data["shell"] = value self._field_shell = FIELD_TO_PARSE @property def dirs(self): if self._field_dirs is FIELD_TO_PARSE: - if 'dirs' in self.raw_data: - dirs = self.raw_data['dirs'] + if "dirs" in self.raw_data: + dirs = self.raw_data["dirs"] if not isinstance(dirs, dict): - self.raise_error('must be a table') + self.raise_error("must be a table") - self._field_dirs = DirsConfig(dirs, ('dirs',)) + self._field_dirs = DirsConfig(dirs, ("dirs",)) else: dirs = {} - self.raw_data['dirs'] = dirs - self._field_dirs = DirsConfig(dirs, ('dirs',)) + self.raw_data["dirs"] = dirs + self._field_dirs = DirsConfig(dirs, ("dirs",)) return self._field_dirs @dirs.setter def dirs(self, value): - self.raw_data['dirs'] = value + self.raw_data["dirs"] = value self._field_dirs = FIELD_TO_PARSE @property def projects(self): if self._field_projects is FIELD_TO_PARSE: - if 'projects' in self.raw_data: - projects = self.raw_data['projects'] + if "projects" in self.raw_data: + projects = self.raw_data["projects"] if not isinstance(projects, dict): - self.raise_error('must be a table') + self.raise_error("must be a table") project_data = {} for name, data in projects.items(): if isinstance(data, str): - project_data[name] = ProjectConfig({'location': data}, ('projects', name)) + project_data[name] = ProjectConfig({"location": data}, ("projects", name)) elif isinstance(data, dict): - project_data[name] = ProjectConfig(data, ('projects', name)) + project_data[name] = ProjectConfig(data, ("projects", name)) else: - self.raise_error('must be a string or table', extra_steps=(name,)) + self.raise_error("must be a string or table", extra_steps=(name,)) self._field_projects = project_data else: - self._field_projects = self.raw_data['projects'] = {} + self._field_projects = self.raw_data["projects"] = {} return self._field_projects @projects.setter def projects(self, value): - self.raw_data['projects'] = value + self.raw_data["projects"] = value self._field_projects = FIELD_TO_PARSE @property def publish(self): if self._field_publish is FIELD_TO_PARSE: - if 'publish' in self.raw_data: - publish = self.raw_data['publish'] + if "publish" in self.raw_data: + publish = self.raw_data["publish"] if not isinstance(publish, dict): - self.raise_error('must be a table') + self.raise_error("must be a table") for name, data in publish.items(): if not isinstance(data, dict): - self.raise_error('must be a table', extra_steps=(name,)) + self.raise_error("must be a table", extra_steps=(name,)) self._field_publish = publish else: - self._field_publish = self.raw_data['publish'] = {'index': {'repo': 'main'}} + self._field_publish = self.raw_data["publish"] = {"index": {"repo": "main"}} return self._field_publish @publish.setter def publish(self, value): - self.raw_data['publish'] = value + self.raw_data["publish"] = value self._field_publish = FIELD_TO_PARSE @property def template(self): if self._field_template is FIELD_TO_PARSE: - if 'template' in self.raw_data: - template = self.raw_data['template'] + if "template" in self.raw_data: + template = self.raw_data["template"] if not isinstance(template, dict): - self.raise_error('must be a table') + self.raise_error("must be a table") - self._field_template = TemplateConfig(template, ('template',)) + self._field_template = TemplateConfig(template, ("template",)) else: template = {} - self.raw_data['template'] = template - self._field_template = TemplateConfig(template, ('template',)) + self.raw_data["template"] = template + self._field_template = TemplateConfig(template, ("template",)) return self._field_template @template.setter def template(self, value): - self.raw_data['template'] = value + self.raw_data["template"] = value self._field_template = FIELD_TO_PARSE @property def terminal(self): if self._field_terminal is FIELD_TO_PARSE: - if 'terminal' in self.raw_data: - terminal = self.raw_data['terminal'] + if "terminal" in self.raw_data: + terminal = self.raw_data["terminal"] if not isinstance(terminal, dict): - self.raise_error('must be a table') + self.raise_error("must be a table") - self._field_terminal = TerminalConfig(terminal, ('terminal',)) + self._field_terminal = TerminalConfig(terminal, ("terminal",)) else: terminal = {} - self.raw_data['terminal'] = terminal - self._field_terminal = TerminalConfig(terminal, ('terminal',)) + self.raw_data["terminal"] = terminal + self._field_terminal = TerminalConfig(terminal, ("terminal",)) return self._field_terminal @terminal.setter def terminal(self, value): - self.raw_data['terminal'] = value + self.raw_data["terminal"] = value self._field_terminal = FIELD_TO_PARSE @@ -244,62 +244,62 @@ def __init__(self, *args, **kwargs): @property def name(self): if self._field_name is FIELD_TO_PARSE: - if 'name' in self.raw_data: - name = self.raw_data['name'] + if "name" in self.raw_data: + name = self.raw_data["name"] if not isinstance(name, str): - self.raise_error('must be a string') + self.raise_error("must be a string") self._field_name = name else: - self.raise_error('required field') + self.raise_error("required field") return self._field_name @name.setter def name(self, value): - self.raw_data['name'] = value + self.raw_data["name"] = value self._field_name = FIELD_TO_PARSE @property def path(self): if self._field_path is FIELD_TO_PARSE: - if 'path' in self.raw_data: - path = self.raw_data['path'] + if "path" in self.raw_data: + path = self.raw_data["path"] if not isinstance(path, str): - self.raise_error('must be a string') + self.raise_error("must be a string") self._field_path = path else: - self._field_path = self.raw_data['path'] = self.name + self._field_path = self.raw_data["path"] = self.name return self._field_path @path.setter def path(self, value): - self.raw_data['path'] = value + self.raw_data["path"] = value self._field_path = FIELD_TO_PARSE @property def args(self): if self._field_args is FIELD_TO_PARSE: - if 'args' in self.raw_data: - args = self.raw_data['args'] + if "args" in self.raw_data: + args = self.raw_data["args"] if not isinstance(args, list): - self.raise_error('must be an array') + self.raise_error("must be an array") for i, entry in enumerate(args, 1): if not isinstance(entry, str): - self.raise_error('must be a string', extra_steps=(str(i),)) + self.raise_error("must be a string", extra_steps=(str(i),)) self._field_args = args else: - self._field_args = self.raw_data['args'] = [] + self._field_args = self.raw_data["args"] = [] return self._field_args @args.setter def args(self, value): - self.raw_data['args'] = value + self.raw_data["args"] = value self._field_args = FIELD_TO_PARSE @@ -316,108 +316,108 @@ def __init__(self, *args, **kwargs): @property def project(self): if self._field_project is FIELD_TO_PARSE: - if 'project' in self.raw_data: - project = self.raw_data['project'] + if "project" in self.raw_data: + project = self.raw_data["project"] if not isinstance(project, list): - self.raise_error('must be an array') + self.raise_error("must be an array") for i, entry in enumerate(project, 1): if not isinstance(entry, str): - self.raise_error('must be a string', extra_steps=(str(i),)) + self.raise_error("must be a string", extra_steps=(str(i),)) self._field_project = project else: - self._field_project = self.raw_data['project'] = [] + self._field_project = self.raw_data["project"] = [] return self._field_project @project.setter def project(self, value): - self.raw_data['project'] = value + self.raw_data["project"] = value self._field_project = FIELD_TO_PARSE @property def env(self): if self._field_env is FIELD_TO_PARSE: - if 'env' in self.raw_data: - env = self.raw_data['env'] + if "env" in self.raw_data: + env = self.raw_data["env"] if not isinstance(env, dict): - self.raise_error('must be a table') + self.raise_error("must be a table") for key, value in env.items(): if not isinstance(value, str): - self.raise_error('must be a string', extra_steps=(key,)) + self.raise_error("must be a string", extra_steps=(key,)) self._field_env = env else: - self._field_env = self.raw_data['env'] = {} + self._field_env = self.raw_data["env"] = {} return self._field_env @env.setter def env(self, value): - self.raw_data['env'] = value + self.raw_data["env"] = value self._field_env = FIELD_TO_PARSE @property def python(self): if self._field_python is FIELD_TO_PARSE: - if 'python' in self.raw_data: - python = self.raw_data['python'] + if "python" in self.raw_data: + python = self.raw_data["python"] if not isinstance(python, str): - self.raise_error('must be a string') + self.raise_error("must be a string") self._field_python = python else: - self._field_python = self.raw_data['python'] = 'isolated' + self._field_python = self.raw_data["python"] = "isolated" return self._field_python @python.setter def python(self, value): - self.raw_data['python'] = value + self.raw_data["python"] = value self._field_python = FIELD_TO_PARSE @property def data(self): if self._field_data is FIELD_TO_PARSE: - if 'data' in self.raw_data: - data = self.raw_data['data'] + if "data" in self.raw_data: + data = self.raw_data["data"] if not isinstance(data, str): - self.raise_error('must be a string') + self.raise_error("must be a string") self._field_data = data else: from platformdirs import user_data_dir - self._field_data = self.raw_data['data'] = user_data_dir('hatch', appauthor=False) + self._field_data = self.raw_data["data"] = user_data_dir("hatch", appauthor=False) return self._field_data @data.setter def data(self, value): - self.raw_data['data'] = value + self.raw_data["data"] = value self._field_data = FIELD_TO_PARSE @property def cache(self): if self._field_cache is FIELD_TO_PARSE: - if 'cache' in self.raw_data: - cache = self.raw_data['cache'] + if "cache" in self.raw_data: + cache = self.raw_data["cache"] if not isinstance(cache, str): - self.raise_error('must be a string') + self.raise_error("must be a string") self._field_cache = cache else: from platformdirs import user_cache_dir - self._field_cache = self.raw_data['cache'] = user_cache_dir('hatch', appauthor=False) + self._field_cache = self.raw_data["cache"] = user_cache_dir("hatch", appauthor=False) return self._field_cache @cache.setter def cache(self, value): - self.raw_data['cache'] = value + self.raw_data["cache"] = value self._field_cache = FIELD_TO_PARSE @@ -430,20 +430,20 @@ def __init__(self, *args, **kwargs): @property def location(self): if self._field_location is FIELD_TO_PARSE: - if 'location' in self.raw_data: - location = self.raw_data['location'] + if "location" in self.raw_data: + location = self.raw_data["location"] if not isinstance(location, str): - self.raise_error('must be a string') + self.raise_error("must be a string") self._field_location = location else: - self.raise_error('required field') + self.raise_error("required field") return self._field_location @location.setter def location(self, value): - self.raw_data['location'] = value + self.raw_data["location"] = value self._field_location = FIELD_TO_PARSE @@ -459,109 +459,109 @@ def __init__(self, *args, **kwargs): @property def name(self): if self._field_name is FIELD_TO_PARSE: - if 'name' in self.raw_data: - name = self.raw_data['name'] + if "name" in self.raw_data: + name = self.raw_data["name"] if not isinstance(name, str): - self.raise_error('must be a string') + self.raise_error("must be a string") self._field_name = name else: - name = os.environ.get('GIT_AUTHOR_NAME') + name = os.environ.get("GIT_AUTHOR_NAME") if name is None: import subprocess try: name = subprocess.check_output( - ['git', 'config', '--get', 'user.name'], # noqa: S607 + ["git", "config", "--get", "user.name"], # noqa: S607 text=True, ).strip() except Exception: # noqa: BLE001 - name = 'U.N. Owen' + name = "U.N. Owen" - self._field_name = self.raw_data['name'] = name + self._field_name = self.raw_data["name"] = name return self._field_name @name.setter def name(self, value): - self.raw_data['name'] = value + self.raw_data["name"] = value self._field_name = FIELD_TO_PARSE @property def email(self): if self._field_email is FIELD_TO_PARSE: - if 'email' in self.raw_data: - email = self.raw_data['email'] + if "email" in self.raw_data: + email = self.raw_data["email"] if not isinstance(email, str): - self.raise_error('must be a string') + self.raise_error("must be a string") self._field_email = email else: - email = os.environ.get('GIT_AUTHOR_EMAIL') + email = os.environ.get("GIT_AUTHOR_EMAIL") if email is None: import subprocess try: email = subprocess.check_output( - ['git', 'config', '--get', 'user.email'], # noqa: S607 + ["git", "config", "--get", "user.email"], # noqa: S607 text=True, ).strip() except Exception: # noqa: BLE001 - email = 'void@some.where' + email = "void@some.where" - self._field_email = self.raw_data['email'] = email + self._field_email = self.raw_data["email"] = email return self._field_email @email.setter def email(self, value): - self.raw_data['email'] = value + self.raw_data["email"] = value self._field_email = FIELD_TO_PARSE @property def licenses(self): if self._field_licenses is FIELD_TO_PARSE: - if 'licenses' in self.raw_data: - licenses = self.raw_data['licenses'] + if "licenses" in self.raw_data: + licenses = self.raw_data["licenses"] if not isinstance(licenses, dict): - self.raise_error('must be a table') + self.raise_error("must be a table") - self._field_licenses = LicensesConfig(licenses, (*self.steps, 'licenses')) + self._field_licenses = LicensesConfig(licenses, (*self.steps, "licenses")) else: licenses = {} - self.raw_data['licenses'] = licenses - self._field_licenses = LicensesConfig(licenses, (*self.steps, 'licenses')) + self.raw_data["licenses"] = licenses + self._field_licenses = LicensesConfig(licenses, (*self.steps, "licenses")) return self._field_licenses @licenses.setter def licenses(self, value): - self.raw_data['licenses'] = value + self.raw_data["licenses"] = value self._field_licenses = FIELD_TO_PARSE @property def plugins(self): if self._field_plugins is FIELD_TO_PARSE: - if 'plugins' in self.raw_data: - plugins = self.raw_data['plugins'] + if "plugins" in self.raw_data: + plugins = self.raw_data["plugins"] if not isinstance(plugins, dict): - self.raise_error('must be a table') + self.raise_error("must be a table") for name, data in plugins.items(): if not isinstance(data, dict): - self.raise_error('must be a table', extra_steps=(name,)) + self.raise_error("must be a table", extra_steps=(name,)) self._field_plugins = plugins else: - self._field_plugins = self.raw_data['plugins'] = { - 'default': {'tests': True, 'ci': False, 'src-layout': True} + self._field_plugins = self.raw_data["plugins"] = { + "default": {"tests": True, "ci": False, "src-layout": True} } return self._field_plugins @plugins.setter def plugins(self, value): - self.raw_data['plugins'] = value + self.raw_data["plugins"] = value self._field_plugins = FIELD_TO_PARSE @@ -575,43 +575,43 @@ def __init__(self, *args, **kwargs): @property def headers(self): if self._field_headers is FIELD_TO_PARSE: - if 'headers' in self.raw_data: - headers = self.raw_data['headers'] + if "headers" in self.raw_data: + headers = self.raw_data["headers"] if not isinstance(headers, bool): - self.raise_error('must be a boolean') + self.raise_error("must be a boolean") self._field_headers = headers else: - self._field_headers = self.raw_data['headers'] = True + self._field_headers = self.raw_data["headers"] = True return self._field_headers @headers.setter def headers(self, value): - self.raw_data['headers'] = value + self.raw_data["headers"] = value self._field_headers = FIELD_TO_PARSE @property def default(self): if self._field_default is FIELD_TO_PARSE: - if 'default' in self.raw_data: - default = self.raw_data['default'] + if "default" in self.raw_data: + default = self.raw_data["default"] if not isinstance(default, list): - self.raise_error('must be an array') + self.raise_error("must be an array") for i, entry in enumerate(default, 1): if not isinstance(entry, str): - self.raise_error('must be a string', extra_steps=(str(i),)) + self.raise_error("must be a string", extra_steps=(str(i),)) self._field_default = default else: - self._field_default = self.raw_data['default'] = ['MIT'] + self._field_default = self.raw_data["default"] = ["MIT"] return self._field_default @default.setter def default(self, value): - self.raw_data['default'] = value + self.raw_data["default"] = value self._field_default = FIELD_TO_PARSE @@ -624,22 +624,22 @@ def __init__(self, *args, **kwargs): @property def styles(self): if self._field_styles is FIELD_TO_PARSE: - if 'styles' in self.raw_data: - styles = self.raw_data['styles'] + if "styles" in self.raw_data: + styles = self.raw_data["styles"] if not isinstance(styles, dict): - self.raise_error('must be a table') + self.raise_error("must be a table") - self._field_styles = StylesConfig(styles, (*self.steps, 'styles')) + self._field_styles = StylesConfig(styles, (*self.steps, "styles")) else: styles = {} - self.raw_data['styles'] = styles - self._field_styles = StylesConfig(styles, (*self.steps, 'styles')) + self.raw_data["styles"] = styles + self._field_styles = StylesConfig(styles, (*self.steps, "styles")) return self._field_styles @styles.setter def styles(self, value): - self.raw_data['styles'] = value + self.raw_data["styles"] = value self._field_styles = FIELD_TO_PARSE @@ -658,132 +658,132 @@ def __init__(self, *args, **kwargs): @property def info(self): if self._field_info is FIELD_TO_PARSE: - if 'info' in self.raw_data: - info = self.raw_data['info'] + if "info" in self.raw_data: + info = self.raw_data["info"] if not isinstance(info, str): - self.raise_error('must be a string') + self.raise_error("must be a string") self._field_info = info else: - self._field_info = self.raw_data['info'] = 'bold' + self._field_info = self.raw_data["info"] = "bold" return self._field_info @info.setter def info(self, value): - self.raw_data['info'] = value + self.raw_data["info"] = value self._field_info = FIELD_TO_PARSE @property def success(self): if self._field_success is FIELD_TO_PARSE: - if 'success' in self.raw_data: - success = self.raw_data['success'] + if "success" in self.raw_data: + success = self.raw_data["success"] if not isinstance(success, str): - self.raise_error('must be a string') + self.raise_error("must be a string") self._field_success = success else: - self._field_success = self.raw_data['success'] = 'bold cyan' + self._field_success = self.raw_data["success"] = "bold cyan" return self._field_success @success.setter def success(self, value): - self.raw_data['success'] = value + self.raw_data["success"] = value self._field_success = FIELD_TO_PARSE @property def error(self): if self._field_error is FIELD_TO_PARSE: - if 'error' in self.raw_data: - error = self.raw_data['error'] + if "error" in self.raw_data: + error = self.raw_data["error"] if not isinstance(error, str): - self.raise_error('must be a string') + self.raise_error("must be a string") self._field_error = error else: - self._field_error = self.raw_data['error'] = 'bold red' + self._field_error = self.raw_data["error"] = "bold red" return self._field_error @error.setter def error(self, value): - self.raw_data['error'] = value + self.raw_data["error"] = value self._field_error = FIELD_TO_PARSE @property def warning(self): if self._field_warning is FIELD_TO_PARSE: - if 'warning' in self.raw_data: - warning = self.raw_data['warning'] + if "warning" in self.raw_data: + warning = self.raw_data["warning"] if not isinstance(warning, str): - self.raise_error('must be a string') + self.raise_error("must be a string") self._field_warning = warning else: - self._field_warning = self.raw_data['warning'] = 'bold yellow' + self._field_warning = self.raw_data["warning"] = "bold yellow" return self._field_warning @warning.setter def warning(self, value): - self.raw_data['warning'] = value + self.raw_data["warning"] = value self._field_warning = FIELD_TO_PARSE @property def waiting(self): if self._field_waiting is FIELD_TO_PARSE: - if 'waiting' in self.raw_data: - waiting = self.raw_data['waiting'] + if "waiting" in self.raw_data: + waiting = self.raw_data["waiting"] if not isinstance(waiting, str): - self.raise_error('must be a string') + self.raise_error("must be a string") self._field_waiting = waiting else: - self._field_waiting = self.raw_data['waiting'] = 'bold magenta' + self._field_waiting = self.raw_data["waiting"] = "bold magenta" return self._field_waiting @waiting.setter def waiting(self, value): - self.raw_data['waiting'] = value + self.raw_data["waiting"] = value self._field_waiting = FIELD_TO_PARSE @property def debug(self): if self._field_debug is FIELD_TO_PARSE: - if 'debug' in self.raw_data: - debug = self.raw_data['debug'] + if "debug" in self.raw_data: + debug = self.raw_data["debug"] if not isinstance(debug, str): - self.raise_error('must be a string') + self.raise_error("must be a string") self._field_debug = debug else: - self._field_debug = self.raw_data['debug'] = 'bold' + self._field_debug = self.raw_data["debug"] = "bold" return self._field_debug @debug.setter def debug(self, value): - self.raw_data['debug'] = value + self.raw_data["debug"] = value self._field_debug = FIELD_TO_PARSE @property def spinner(self): if self._field_spinner is FIELD_TO_PARSE: - if 'spinner' in self.raw_data: - spinner = self.raw_data['spinner'] + if "spinner" in self.raw_data: + spinner = self.raw_data["spinner"] if not isinstance(spinner, str): - self.raise_error('must be a string') + self.raise_error("must be a string") self._field_spinner = spinner else: - self._field_spinner = self.raw_data['spinner'] = 'simpleDotsScrolling' + self._field_spinner = self.raw_data["spinner"] = "simpleDotsScrolling" return self._field_spinner @spinner.setter def spinner(self, value): - self.raw_data['spinner'] = value + self.raw_data["spinner"] = value self._field_spinner = FIELD_TO_PARSE diff --git a/src/hatch/config/user.py b/src/hatch/config/user.py index 70d4fd365..d601ac1bb 100644 --- a/src/hatch/config/user.py +++ b/src/hatch/config/user.py @@ -30,19 +30,19 @@ def save(self, content=None): content = tomli_w.dumps(self.model.raw_data) self.path.ensure_parent_dir_exists() - self.path.write_atomic(content, 'w', encoding='utf-8') + self.path.write_atomic(content, "w", encoding="utf-8") def load(self): self.model = RootConfig(load_toml_data(self.read())) def read(self) -> str: - return self.path.read_text('utf-8') + return self.path.read_text("utf-8") def read_scrubbed(self) -> str: import tomli_w config = RootConfig(load_toml_data(self.read())) - config.raw_data.pop('publish', None) + config.raw_data.pop("publish", None) return tomli_w.dumps(config.raw_data) def restore(self): @@ -64,4 +64,4 @@ def update(self): # no cov def get_default_location(cls) -> Path: from platformdirs import user_config_dir - return Path(user_config_dir('hatch', appauthor=False)) / 'config.toml' + return Path(user_config_dir("hatch", appauthor=False)) / "config.toml" diff --git a/src/hatch/config/utils.py b/src/hatch/config/utils.py index c1fed4270..2d302df8f 100644 --- a/src/hatch/config/utils.py +++ b/src/hatch/config/utils.py @@ -13,7 +13,7 @@ def save_toml_document(document: TOMLDocument, path: Path): path.ensure_parent_dir_exists() - path.write_atomic(tomlkit.dumps(document), 'w', encoding='utf-8') + path.write_atomic(tomlkit.dumps(document), "w", encoding="utf-8") def create_toml_document(config: dict) -> InlineTable: diff --git a/src/hatch/dep/sync.py b/src/hatch/dep/sync.py index 65101603d..4b360c291 100644 --- a/src/hatch/dep/sync.py +++ b/src/hatch/dep/sync.py @@ -13,10 +13,10 @@ def __init__(self, sys_path: list[str]) -> None: self._resolver = Distribution.discover(context=DistributionFinder.Context(path=sys_path)) self._distributions: dict[str, Distribution] = {} self._search_exhausted = False - self._canonical_regex = re.compile(r'[-_.]+') + self._canonical_regex = re.compile(r"[-_.]+") def __getitem__(self, item: str) -> Distribution | None: - item = self._canonical_regex.sub('-', item).lower() + item = self._canonical_regex.sub("-", item).lower() possible_distribution = self._distributions.get(item) if possible_distribution is not None: return possible_distribution @@ -27,11 +27,11 @@ def __getitem__(self, item: str) -> Distribution | None: return None for distribution in self._resolver: - name = distribution.metadata['Name'] + name = distribution.metadata["Name"] if name is None: continue - name = self._canonical_regex.sub('-', name).lower() + name = self._canonical_regex.sub("-", name).lower() self._distributions[name] = distribution if name == item: return distribution @@ -53,11 +53,11 @@ def dependency_in_sync( extras = requirement.extras if extras: - transitive_requirements: list[str] = distribution.metadata.get_all('Requires-Dist', []) + transitive_requirements: list[str] = distribution.metadata.get_all("Requires-Dist", []) if not transitive_requirements: return False - available_extras: list[str] = distribution.metadata.get_all('Provides-Extra', []) + available_extras: list[str] = distribution.metadata.get_all("Provides-Extra", []) for requirement_string in transitive_requirements: transitive_requirement = Requirement(requirement_string) @@ -71,7 +71,7 @@ def dependency_in_sync( return False extra_environment = dict(environment) - extra_environment['extra'] = extra + extra_environment["extra"] = extra if not dependency_in_sync(transitive_requirement, extra_environment, installed_distributions): return False @@ -80,30 +80,30 @@ def dependency_in_sync( # TODO: handle https://discuss.python.org/t/11938 if requirement.url: - direct_url_file = distribution.read_text('direct_url.json') + direct_url_file = distribution.read_text("direct_url.json") if direct_url_file is not None: import json # https://packaging.python.org/specifications/direct-url/ direct_url_data = json.loads(direct_url_file) - if 'vcs_info' in direct_url_data: - url = direct_url_data['url'] - vcs_info = direct_url_data['vcs_info'] - vcs = vcs_info['vcs'] - commit_id = vcs_info['commit_id'] - requested_revision = vcs_info.get('requested_revision') + if "vcs_info" in direct_url_data: + url = direct_url_data["url"] + vcs_info = direct_url_data["vcs_info"] + vcs = vcs_info["vcs"] + commit_id = vcs_info["commit_id"] + requested_revision = vcs_info.get("requested_revision") # Try a few variations, see https://peps.python.org/pep-0440/#direct-references if ( - requested_revision and requirement.url == f'{vcs}+{url}@{requested_revision}#{commit_id}' - ) or requirement.url == f'{vcs}+{url}@{commit_id}': + requested_revision and requirement.url == f"{vcs}+{url}@{requested_revision}#{commit_id}" + ) or requirement.url == f"{vcs}+{url}@{commit_id}": return True - if requirement.url in {f'{vcs}+{url}', f'{vcs}+{url}@{requested_revision}'}: + if requirement.url in {f"{vcs}+{url}", f"{vcs}+{url}@{requested_revision}"}: import subprocess - if vcs == 'git': - vcs_cmd = [vcs, 'ls-remote', url] + if vcs == "git": + vcs_cmd = [vcs, "ls-remote", url] if requested_revision: vcs_cmd.append(requested_revision) # TODO: add elifs for hg, svn, and bzr https://github.com/pypa/hatch/issues/760 diff --git a/src/hatch/env/collectors/custom.py b/src/hatch/env/collectors/custom.py index bfbc11a6f..a6816e83e 100644 --- a/src/hatch/env/collectors/custom.py +++ b/src/hatch/env/collectors/custom.py @@ -9,7 +9,7 @@ class CustomEnvironmentCollector: - PLUGIN_NAME = 'custom' + PLUGIN_NAME = "custom" def __new__( # type: ignore[misc] cls, @@ -18,22 +18,22 @@ def __new__( # type: ignore[misc] *args: Any, **kwargs: Any, ) -> EnvironmentCollectorInterface: - custom_script = config.get('path', DEFAULT_CUSTOM_SCRIPT) + custom_script = config.get("path", DEFAULT_CUSTOM_SCRIPT) if not isinstance(custom_script, str): - message = f'Option `path` for environment collector `{cls.PLUGIN_NAME}` must be a string' + message = f"Option `path` for environment collector `{cls.PLUGIN_NAME}` must be a string" raise TypeError(message) if not custom_script: - message = f'Option `path` for environment collector `{cls.PLUGIN_NAME}` must not be empty if defined' + message = f"Option `path` for environment collector `{cls.PLUGIN_NAME}` must not be empty if defined" raise ValueError(message) path = os.path.normpath(os.path.join(root, custom_script)) if not os.path.isfile(path): - message = f'Plugin script does not exist: {custom_script}' + message = f"Plugin script does not exist: {custom_script}" raise OSError(message) hook_class = load_plugin_from_script( - path, custom_script, EnvironmentCollectorInterface, 'environment_collector' + path, custom_script, EnvironmentCollectorInterface, "environment_collector" ) hook = hook_class(root, config, *args, **kwargs) diff --git a/src/hatch/env/collectors/default.py b/src/hatch/env/collectors/default.py index 97b49b259..9361e162d 100644 --- a/src/hatch/env/collectors/default.py +++ b/src/hatch/env/collectors/default.py @@ -4,10 +4,10 @@ class DefaultEnvironmentCollector(EnvironmentCollectorInterface): - PLUGIN_NAME = 'default' + PLUGIN_NAME = "default" def get_initial_config(self): # noqa: PLR6301 default_config = {} ensure_valid_environment(default_config) - return {'default': default_config, **get_internal_env_config()} + return {"default": default_config, **get_internal_env_config()} diff --git a/src/hatch/env/collectors/plugin/interface.py b/src/hatch/env/collectors/plugin/interface.py index 094f49dd0..e7df60bc9 100644 --- a/src/hatch/env/collectors/plugin/interface.py +++ b/src/hatch/env/collectors/plugin/interface.py @@ -26,7 +26,7 @@ def hatch_register_environment_collector(): ``` """ - PLUGIN_NAME = '' + PLUGIN_NAME = "" """The name used for selection.""" def __init__(self, root, config): diff --git a/src/hatch/env/context.py b/src/hatch/env/context.py index 0f385ae68..d1fdeb7fb 100644 --- a/src/hatch/env/context.py +++ b/src/hatch/env/context.py @@ -13,7 +13,7 @@ def formatters(self): class EnvironmentContextFormatter(EnvironmentContextFormatterBase): def __init__(self, environment): self.environment = environment - self.CONTEXT_NAME = f'environment_{environment.PLUGIN_NAME}' + self.CONTEXT_NAME = f"environment_{environment.PLUGIN_NAME}" def formatters(self): # noqa: PLR6301 """ @@ -27,11 +27,11 @@ def formatters(self): # noqa: PLR6301 def get_formatters(self): formatters = { - 'args': self.__format_args, - 'env_name': self.__format_env_name, - 'env_type': self.__format_env_type, - 'matrix': self.__format_matrix, - 'verbosity': self.__format_verbosity, + "args": self.__format_args, + "env_name": self.__format_env_name, + "env_type": self.__format_env_type, + "matrix": self.__format_matrix, + "verbosity": self.__format_verbosity, } formatters.update(self.formatters()) return formatters @@ -40,7 +40,7 @@ def __format_args(self, value, data): # noqa: PLR6301 if value is not None: return value - return data or '' + return data or "" def __format_env_name(self, value, data): # noqa: ARG002 return self.environment.name @@ -50,15 +50,15 @@ def __format_env_type(self, value, data): # noqa: ARG002 def __format_matrix(self, value, data): # noqa: ARG002 if not data: - message = 'The `matrix` context formatting field requires a modifier' + message = "The `matrix` context formatting field requires a modifier" raise ValueError(message) - variable, separator, default = data.partition(':') + variable, separator, default = data.partition(":") if variable in self.environment.matrix_variables: return self.environment.matrix_variables[variable] if not separator: - message = f'Nonexistent matrix variable must set a default: {variable}' + message = f"Nonexistent matrix variable must set a default: {variable}" raise ValueError(message) return default @@ -67,18 +67,18 @@ def __format_verbosity(self, value, data): # noqa: ARG002 if not data: return str(self.environment.verbosity) - modifier, _, adjustment = data.partition(':') - if modifier != 'flag': - message = f'Unknown verbosity modifier: {modifier}' + modifier, _, adjustment = data.partition(":") + if modifier != "flag": + message = f"Unknown verbosity modifier: {modifier}" raise ValueError(message) if not adjustment: - adjustment = '0' + adjustment = "0" try: adjustment = int(adjustment) except ValueError: - message = f'Verbosity flag adjustment must be an integer: {adjustment}' + message = f"Verbosity flag adjustment must be an integer: {adjustment}" raise TypeError(message) from None return get_verbosity_flag(self.environment.verbosity, adjustment=adjustment) diff --git a/src/hatch/env/internal/__init__.py b/src/hatch/env/internal/__init__.py index 7010f0b24..9aa1cbf3a 100644 --- a/src/hatch/env/internal/__init__.py +++ b/src/hatch/env/internal/__init__.py @@ -10,12 +10,12 @@ def get_internal_env_config() -> dict[str, Any]: internal_config = {} for env_name, env_config in ( - ('hatch-build', build.get_default_config()), - ('hatch-static-analysis', static_analysis.get_default_config()), - ('hatch-test', test.get_default_config()), - ('hatch-uv', uv.get_default_config()), + ("hatch-build", build.get_default_config()), + ("hatch-static-analysis", static_analysis.get_default_config()), + ("hatch-test", test.get_default_config()), + ("hatch-uv", uv.get_default_config()), ): - env_config['template'] = env_name + env_config["template"] = env_name ensure_valid_environment(env_config) internal_config[env_name] = env_config @@ -34,8 +34,8 @@ def is_isolated_environment(env_name: str, config: dict[str, Any]) -> bool: # improve responsiveness. However, if the user for some reason chooses to override the dependencies # to use a different version of Ruff, then the project would get its own environment. return ( - not config.get('builder', False) - and config.get('skip-install', False) + not config.get("builder", False) + and config.get("skip-install", False) and is_default_environment(env_name, config) ) @@ -45,12 +45,12 @@ def is_default_environment(env_name: str, config: dict[str, Any]) -> bool: internal_config = get_internal_env_config().get(env_name) if not internal_config: # Environment generated from matrix - internal_config = get_internal_env_config().get(env_name.split('.')[0]) + internal_config = get_internal_env_config().get(env_name.split(".")[0]) if not internal_config: return False # Only consider things that would modify the actual installation, other options like extra scripts don't matter - for key in ('dependencies', 'extra-dependencies', 'features'): + for key in ("dependencies", "extra-dependencies", "features"): if config.get(key) != internal_config.get(key): return False diff --git a/src/hatch/env/internal/build.py b/src/hatch/env/internal/build.py index 58170efe7..aac6eac3b 100644 --- a/src/hatch/env/internal/build.py +++ b/src/hatch/env/internal/build.py @@ -5,7 +5,7 @@ def get_default_config() -> dict[str, Any]: return { - 'skip-install': True, - 'builder': True, - 'installer': 'uv', + "skip-install": True, + "builder": True, + "installer": "uv", } diff --git a/src/hatch/env/internal/static_analysis.py b/src/hatch/env/internal/static_analysis.py index c4bd01a42..46f70948c 100644 --- a/src/hatch/env/internal/static_analysis.py +++ b/src/hatch/env/internal/static_analysis.py @@ -5,16 +5,16 @@ def get_default_config() -> dict[str, Any]: return { - 'skip-install': True, - 'installer': 'uv', - 'dependencies': [f'ruff=={RUFF_DEFAULT_VERSION}'], - 'scripts': { - 'format-check': 'ruff format{env:HATCH_FMT_ARGS:} --check --diff {args:.}', - 'format-fix': 'ruff format{env:HATCH_FMT_ARGS:} {args:.}', - 'lint-check': 'ruff check{env:HATCH_FMT_ARGS:} {args:.}', - 'lint-fix': 'ruff check{env:HATCH_FMT_ARGS:} --fix {args:.}', + "skip-install": True, + "installer": "uv", + "dependencies": [f"ruff=={RUFF_DEFAULT_VERSION}"], + "scripts": { + "format-check": "ruff format{env:HATCH_FMT_ARGS:} --check --diff {args:.}", + "format-fix": "ruff format{env:HATCH_FMT_ARGS:} {args:.}", + "lint-check": "ruff check{env:HATCH_FMT_ARGS:} {args:.}", + "lint-fix": "ruff check{env:HATCH_FMT_ARGS:} --fix {args:.}", }, } -RUFF_DEFAULT_VERSION: str = '0.6.8' +RUFF_DEFAULT_VERSION: str = "0.13.2" diff --git a/src/hatch/env/internal/test.py b/src/hatch/env/internal/test.py index d507b66e5..d073f94c4 100644 --- a/src/hatch/env/internal/test.py +++ b/src/hatch/env/internal/test.py @@ -5,21 +5,21 @@ def get_default_config() -> dict[str, Any]: return { - 'installer': 'uv', - 'dependencies': [ - 'coverage-enable-subprocess==1.0', - 'coverage[toml]~=7.4', - 'pytest~=8.1', - 'pytest-mock~=3.12', - 'pytest-randomly~=3.15', - 'pytest-rerunfailures~=14.0', - 'pytest-xdist[psutil]~=3.5', + "installer": "uv", + "dependencies": [ + "coverage-enable-subprocess==1.0", + "coverage[toml]~=7.4", + "pytest~=8.1", + "pytest-mock~=3.12", + "pytest-randomly~=3.15", + "pytest-rerunfailures~=14.0", + "pytest-xdist[psutil]~=3.5", ], - 'scripts': { - 'run': 'pytest{env:HATCH_TEST_ARGS:} {args}', - 'run-cov': 'coverage run -m pytest{env:HATCH_TEST_ARGS:} {args}', - 'cov-combine': 'coverage combine', - 'cov-report': 'coverage report', + "scripts": { + "run": "pytest{env:HATCH_TEST_ARGS:} {args}", + "run-cov": "coverage run -m pytest{env:HATCH_TEST_ARGS:} {args}", + "cov-combine": "coverage combine", + "cov-report": "coverage report", }, - 'matrix': [{'python': ['3.13', '3.12', '3.11', '3.10', '3.9', '3.8']}], + "matrix": [{"python": ["3.13", "3.12", "3.11", "3.10", "3.9", "3.8"]}], } diff --git a/src/hatch/env/internal/uv.py b/src/hatch/env/internal/uv.py index 10aac49c0..fc41cc0ee 100644 --- a/src/hatch/env/internal/uv.py +++ b/src/hatch/env/internal/uv.py @@ -5,6 +5,6 @@ def get_default_config() -> dict[str, Any]: return { - 'skip-install': True, - 'installer': 'uv', + "skip-install": True, + "installer": "uv", } diff --git a/src/hatch/env/plugin/interface.py b/src/hatch/env/plugin/interface.py index 7d164b4aa..09678d7c8 100644 --- a/src/hatch/env/plugin/interface.py +++ b/src/hatch/env/plugin/interface.py @@ -44,7 +44,7 @@ def hatch_register_environment(): ``` """ - PLUGIN_NAME = '' + PLUGIN_NAME = "" """The name used for selection.""" def __init__( @@ -166,13 +166,13 @@ def pathsep(self) -> str: @cached_property def system_python(self): system_python = os.environ.get(AppEnvVars.PYTHON) - if system_python == 'self': + if system_python == "self": system_python = sys.executable system_python = ( system_python - or self.platform.modules.shutil.which('python') - or self.platform.modules.shutil.which('python3') + or self.platform.modules.shutil.which("python") + or self.platform.modules.shutil.which("python3") or sys.executable ) if not isabs(system_python): @@ -187,15 +187,15 @@ def env_vars(self) -> dict: [tool.hatch.envs..env-vars] ``` """ - env_vars = self.config.get('env-vars', {}) + env_vars = self.config.get("env-vars", {}) if not isinstance(env_vars, dict): - message = f'Field `tool.hatch.envs.{self.name}.env-vars` must be a mapping' + message = f"Field `tool.hatch.envs.{self.name}.env-vars` must be a mapping" raise TypeError(message) for key, value in env_vars.items(): if not isinstance(value, str): message = ( - f'Environment variable `{key}` of field `tool.hatch.envs.{self.name}.env-vars` must be a string' + f"Environment variable `{key}` of field `tool.hatch.envs.{self.name}.env-vars` must be a string" ) raise TypeError(message) @@ -215,17 +215,17 @@ def env_include(self) -> list[str]: env-include = [...] ``` """ - env_include = self.config.get('env-include', []) + env_include = self.config.get("env-include", []) if not isinstance(env_include, list): - message = f'Field `tool.hatch.envs.{self.name}.env-include` must be an array' + message = f"Field `tool.hatch.envs.{self.name}.env-include` must be an array" raise TypeError(message) for i, pattern in enumerate(env_include, 1): if not isinstance(pattern, str): - message = f'Pattern #{i} of field `tool.hatch.envs.{self.name}.env-include` must be a string' + message = f"Pattern #{i} of field `tool.hatch.envs.{self.name}.env-include` must be a string" raise TypeError(message) - return ['HATCH_BUILD_*', *env_include] if env_include else env_include + return ["HATCH_BUILD_*", *env_include] if env_include else env_include @cached_property def env_exclude(self) -> list[str]: @@ -235,14 +235,14 @@ def env_exclude(self) -> list[str]: env-exclude = [...] ``` """ - env_exclude = self.config.get('env-exclude', []) + env_exclude = self.config.get("env-exclude", []) if not isinstance(env_exclude, list): - message = f'Field `tool.hatch.envs.{self.name}.env-exclude` must be an array' + message = f"Field `tool.hatch.envs.{self.name}.env-exclude` must be an array" raise TypeError(message) for i, pattern in enumerate(env_exclude, 1): if not isinstance(pattern, str): - message = f'Pattern #{i} of field `tool.hatch.envs.{self.name}.env-exclude` must be a string' + message = f"Pattern #{i} of field `tool.hatch.envs.{self.name}.env-exclude` must be a string" raise TypeError(message) return env_exclude @@ -253,21 +253,21 @@ def environment_dependencies_complex(self): dependencies_complex = [] with self.apply_context(): - for option in ('dependencies', 'extra-dependencies'): + for option in ("dependencies", "extra-dependencies"): dependencies = self.config.get(option, []) if not isinstance(dependencies, list): - message = f'Field `tool.hatch.envs.{self.name}.{option}` must be an array' + message = f"Field `tool.hatch.envs.{self.name}.{option}` must be an array" raise TypeError(message) for i, entry in enumerate(dependencies, 1): if not isinstance(entry, str): - message = f'Dependency #{i} of field `tool.hatch.envs.{self.name}.{option}` must be a string' + message = f"Dependency #{i} of field `tool.hatch.envs.{self.name}.{option}` must be a string" raise TypeError(message) try: dependencies_complex.append(Requirement(self.metadata.context.format(entry))) except InvalidRequirement as e: - message = f'Dependency #{i} of field `tool.hatch.envs.{self.name}.{option}` is invalid: {e}' + message = f"Dependency #{i} of field `tool.hatch.envs.{self.name}.{option}` is invalid: {e}" raise ValueError(message) from None return dependencies_complex @@ -301,8 +301,8 @@ def dependencies_complex(self): for feature in self.features: if feature not in optional_dependencies_complex: message = ( - f'Feature `{feature}` of field `tool.hatch.envs.{self.name}.features` is not ' - f'defined in the dynamic field `project.optional-dependencies`' + f"Feature `{feature}` of field `tool.hatch.envs.{self.name}.features` is not " + f"defined in the dynamic field `project.optional-dependencies`" ) raise ValueError(message) @@ -331,14 +331,14 @@ def platforms(self) -> list[str]: platforms = [...] ``` """ - platforms = self.config.get('platforms', []) + platforms = self.config.get("platforms", []) if not isinstance(platforms, list): - message = f'Field `tool.hatch.envs.{self.name}.platforms` must be an array' + message = f"Field `tool.hatch.envs.{self.name}.platforms` must be an array" raise TypeError(message) for i, command in enumerate(platforms, 1): if not isinstance(command, str): - message = f'Platform #{i} of field `tool.hatch.envs.{self.name}.platforms` must be a string' + message = f"Platform #{i} of field `tool.hatch.envs.{self.name}.platforms` must be a string" raise TypeError(message) return [platform.lower() for platform in platforms] @@ -351,9 +351,9 @@ def skip_install(self) -> bool: skip-install = ... ``` """ - skip_install = self.config.get('skip-install', not self.metadata.has_project_file()) + skip_install = self.config.get("skip-install", not self.metadata.has_project_file()) if not isinstance(skip_install, bool): - message = f'Field `tool.hatch.envs.{self.name}.skip-install` must be a boolean' + message = f"Field `tool.hatch.envs.{self.name}.skip-install` must be a boolean" raise TypeError(message) return skip_install @@ -366,9 +366,9 @@ def dev_mode(self) -> bool: dev-mode = ... ``` """ - dev_mode = self.config.get('dev-mode', True) + dev_mode = self.config.get("dev-mode", True) if not isinstance(dev_mode, bool): - message = f'Field `tool.hatch.envs.{self.name}.dev-mode` must be a boolean' + message = f"Field `tool.hatch.envs.{self.name}.dev-mode` must be a boolean" raise TypeError(message) return dev_mode @@ -381,9 +381,9 @@ def builder(self) -> bool: builder = ... ``` """ - builder = self.config.get('builder', False) + builder = self.config.get("builder", False) if not isinstance(builder, bool): - message = f'Field `tool.hatch.envs.{self.name}.builder` must be a boolean' + message = f"Field `tool.hatch.envs.{self.name}.builder` must be a boolean" raise TypeError(message) return builder @@ -392,19 +392,19 @@ def builder(self) -> bool: def features(self): from hatchling.metadata.utils import normalize_project_name - features = self.config.get('features', []) + features = self.config.get("features", []) if not isinstance(features, list): - message = f'Field `tool.hatch.envs.{self.name}.features` must be an array of strings' + message = f"Field `tool.hatch.envs.{self.name}.features` must be an array of strings" raise TypeError(message) all_features = set() for i, feature in enumerate(features, 1): if not isinstance(feature, str): - message = f'Feature #{i} of field `tool.hatch.envs.{self.name}.features` must be a string' + message = f"Feature #{i} of field `tool.hatch.envs.{self.name}.features` must be a string" raise TypeError(message) if not feature: - message = f'Feature #{i} of field `tool.hatch.envs.{self.name}.features` cannot be an empty string' + message = f"Feature #{i} of field `tool.hatch.envs.{self.name}.features` cannot be an empty string" raise ValueError(message) normalized_feature = ( @@ -415,8 +415,8 @@ def features(self): and normalized_feature not in self.metadata.core.optional_dependencies ): message = ( - f'Feature `{normalized_feature}` of field `tool.hatch.envs.{self.name}.features` is not ' - f'defined in field `project.optional-dependencies`' + f"Feature `{normalized_feature}` of field `tool.hatch.envs.{self.name}.features` is not " + f"defined in field `project.optional-dependencies`" ) raise ValueError(message) @@ -432,9 +432,9 @@ def description(self) -> str: description = ... ``` """ - description = self.config.get('description', '') + description = self.config.get("description", "") if not isinstance(description, str): - message = f'Field `tool.hatch.envs.{self.name}.description` must be a string' + message = f"Field `tool.hatch.envs.{self.name}.description` must be a string" raise TypeError(message) return description @@ -444,17 +444,16 @@ def scripts(self): config = {} # Extra scripts should come first to give less precedence - for field in ('extra-scripts', 'scripts'): + for field in ("extra-scripts", "scripts"): script_config = self.config.get(field, {}) if not isinstance(script_config, dict): - message = f'Field `tool.hatch.envs.{self.name}.{field}` must be a table' + message = f"Field `tool.hatch.envs.{self.name}.{field}` must be a table" raise TypeError(message) for name, data in script_config.items(): - if ' ' in name: + if " " in name: message = ( - f'Script name `{name}` in field `tool.hatch.envs.{self.name}.{field}` ' - f'must not contain spaces' + f"Script name `{name}` in field `tool.hatch.envs.{self.name}.{field}` must not contain spaces" ) raise ValueError(message) @@ -466,16 +465,14 @@ def scripts(self): for i, command in enumerate(data, 1): if not isinstance(command, str): message = ( - f'Command #{i} in field `tool.hatch.envs.{self.name}.{field}.{name}` ' - f'must be a string' + f"Command #{i} in field `tool.hatch.envs.{self.name}.{field}.{name}` must be a string" ) raise TypeError(message) commands.append(command) else: message = ( - f'Field `tool.hatch.envs.{self.name}.{field}.{name}` must be ' - f'a string or an array of strings' + f"Field `tool.hatch.envs.{self.name}.{field}.{name}` must be a string or an array of strings" ) raise TypeError(message) @@ -490,28 +487,28 @@ def scripts(self): @cached_property def pre_install_commands(self): - pre_install_commands = self.config.get('pre-install-commands', []) + pre_install_commands = self.config.get("pre-install-commands", []) if not isinstance(pre_install_commands, list): - message = f'Field `tool.hatch.envs.{self.name}.pre-install-commands` must be an array' + message = f"Field `tool.hatch.envs.{self.name}.pre-install-commands` must be an array" raise TypeError(message) for i, command in enumerate(pre_install_commands, 1): if not isinstance(command, str): - message = f'Command #{i} of field `tool.hatch.envs.{self.name}.pre-install-commands` must be a string' + message = f"Command #{i} of field `tool.hatch.envs.{self.name}.pre-install-commands` must be a string" raise TypeError(message) return list(pre_install_commands) @cached_property def post_install_commands(self): - post_install_commands = self.config.get('post-install-commands', []) + post_install_commands = self.config.get("post-install-commands", []) if not isinstance(post_install_commands, list): - message = f'Field `tool.hatch.envs.{self.name}.post-install-commands` must be an array' + message = f"Field `tool.hatch.envs.{self.name}.post-install-commands` must be an array" raise TypeError(message) for i, command in enumerate(post_install_commands, 1): if not isinstance(command, str): - message = f'Command #{i} of field `tool.hatch.envs.{self.name}.post-install-commands` must be a string' + message = f"Command #{i} of field `tool.hatch.envs.{self.name}.post-install-commands` must be a string" raise TypeError(message) return list(post_install_commands) @@ -627,7 +624,7 @@ def app_status_creation(self): """ See the [life cycle of environments](reference.md#life-cycle). """ - with self.app.status(f'Creating environment: {self.name}'): + with self.app.status(f"Creating environment: {self.name}"): yield @contextmanager @@ -635,7 +632,7 @@ def app_status_pre_installation(self): """ See the [life cycle of environments](reference.md#life-cycle). """ - with self.app.status('Running pre-installation commands'): + with self.app.status("Running pre-installation commands"): yield @contextmanager @@ -643,7 +640,7 @@ def app_status_post_installation(self): """ See the [life cycle of environments](reference.md#life-cycle). """ - with self.app.status('Running post-installation commands'): + with self.app.status("Running post-installation commands"): yield @contextmanager @@ -652,10 +649,10 @@ def app_status_project_installation(self): See the [life cycle of environments](reference.md#life-cycle). """ if self.dev_mode: - with self.app.status('Installing project in development mode'): + with self.app.status("Installing project in development mode"): yield else: - with self.app.status('Installing project'): + with self.app.status("Installing project"): yield @contextmanager @@ -664,9 +661,9 @@ def app_status_dependency_state_check(self): See the [life cycle of environments](reference.md#life-cycle). """ if not self.skip_install and ( - 'dependencies' in self.metadata.dynamic or 'optional-dependencies' in self.metadata.dynamic + "dependencies" in self.metadata.dynamic or "optional-dependencies" in self.metadata.dynamic ): - with self.app.status('Polling dependency state'): + with self.app.status("Polling dependency state"): yield else: yield @@ -676,7 +673,7 @@ def app_status_dependency_installation_check(self): """ See the [life cycle of environments](reference.md#life-cycle). """ - with self.app.status('Checking dependencies'): + with self.app.status("Checking dependencies"): yield @contextmanager @@ -684,7 +681,7 @@ def app_status_dependency_synchronization(self): """ See the [life cycle of environments](reference.md#life-cycle). """ - with self.app.status('Syncing dependencies'): + with self.app.status("Syncing dependencies"): yield @contextmanager @@ -722,7 +719,7 @@ def run_shell_command(self, command: str, **kwargs): [command_context](reference.md#hatch.env.plugin.interface.EnvironmentInterface.command_context) is active, with the expectation of providing the same guarantee. """ - kwargs.setdefault('shell', True) + kwargs.setdefault("shell", True) return self.platform.run_command(command, **kwargs) @contextmanager @@ -768,7 +765,7 @@ def construct_pip_install_command(self, args: list[str]): A convenience method for constructing a [`pip install`](https://pip.pypa.io/en/stable/cli/pip_install/) command with the given verbosity. The default verbosity is set to one less than Hatch's verbosity. """ - command = ['python', '-u', '-m', 'pip', 'install', '--disable-pip-version-check'] + command = ["python", "-u", "-m", "pip", "install", "--disable-pip-version-check"] # Default to -1 verbosity add_verbosity_flag(command, self.verbosity, adjustment=-1) @@ -789,8 +786,8 @@ def apply_features(self, requirement: str): to the given requirement. """ if self.features: - features = ','.join(self.features) - return f'{requirement}[{features}]' + features = ",".join(self.features) + return f"{requirement}[{features}]" return requirement @@ -804,7 +801,7 @@ def check_compatibility(self): [created](reference.md#hatch.env.plugin.interface.EnvironmentInterface.create). """ if self.platforms and self.platform.name not in self.platforms: - message = 'unsupported platform' + message = "unsupported platform" raise OSError(message) def get_env_vars(self) -> EnvVars: @@ -893,7 +890,7 @@ def join(self, relative_path: str) -> FileSystemContext: This method should not need overwriting. """ local_path = self.local_path / relative_path - env_path = f'{self.env_path}{self.__env.sep.join(["", *os.path.normpath(relative_path).split(os.sep)])}' + env_path = f"{self.env_path}{self.__env.sep.join(['', *os.path.normpath(relative_path).split(os.sep)])}" return FileSystemContext(self.__env, local_path=local_path, env_path=env_path) def sync_env(self): @@ -917,7 +914,7 @@ def expand_script_commands(env_name, script_name, commands, config, seen, active if script_name in active: active.append(script_name) - message = f'Circular expansion detected for field `tool.hatch.envs.{env_name}.scripts`: {" -> ".join(active)}' + message = f"Circular expansion detected for field `tool.hatch.envs.{env_name}.scripts`: {' -> '.join(active)}" raise ValueError(message) active.append(script_name) diff --git a/src/hatch/env/system.py b/src/hatch/env/system.py index 804e15d2a..dc7a55ff0 100644 --- a/src/hatch/env/system.py +++ b/src/hatch/env/system.py @@ -5,13 +5,13 @@ class SystemEnvironment(EnvironmentInterface): - PLUGIN_NAME = 'system' + PLUGIN_NAME = "system" def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.python_info = PythonInfo(self.platform) - self.install_indicator = self.data_directory / str(self.root).encode('utf-8').hex() + self.install_indicator = self.data_directory / str(self.root).encode("utf-8").hex() def find(self): return os.path.dirname(os.path.dirname(self.system_python)) @@ -30,7 +30,7 @@ def install_project(self): def install_project_dev_mode(self): self.platform.check_command( - self.construct_pip_install_command(['--editable', self.apply_features(str(self.root))]) + self.construct_pip_install_command(["--editable", self.apply_features(str(self.root))]) ) def dependencies_in_sync(self): diff --git a/src/hatch/env/utils.py b/src/hatch/env/utils.py index b7bce6e64..f251407aa 100644 --- a/src/hatch/env/utils.py +++ b/src/hatch/env/utils.py @@ -6,26 +6,26 @@ def get_env_var(*, plugin_name: str, option: str) -> str: - return f'{AppEnvVars.ENV_OPTION_PREFIX}{plugin_name}_{option.replace("-", "_")}'.upper() + return f"{AppEnvVars.ENV_OPTION_PREFIX}{plugin_name}_{option.replace('-', '_')}".upper() -def get_env_var_option(*, plugin_name: str, option: str, default: str = '') -> str: +def get_env_var_option(*, plugin_name: str, option: str, default: str = "") -> str: return os.environ.get(get_env_var(plugin_name=plugin_name, option=option), default) def ensure_valid_environment(env_config: dict): - env_config.setdefault('type', 'virtual') + env_config.setdefault("type", "virtual") def get_verbosity_flag(verbosity: int, *, adjustment=0) -> str: verbosity += adjustment if not verbosity: - return '' + return "" if verbosity > 0: - return f'-{"v" * abs(min(verbosity, 3))}' + return f"-{'v' * abs(min(verbosity, 3))}" - return f'-{"q" * abs(max(verbosity, -3))}' + return f"-{'q' * abs(max(verbosity, -3))}" def add_verbosity_flag(command: list[str], verbosity: int, *, adjustment=0): diff --git a/src/hatch/env/virtual.py b/src/hatch/env/virtual.py index dab5a7ee7..e2afb616f 100644 --- a/src/hatch/env/virtual.py +++ b/src/hatch/env/virtual.py @@ -25,7 +25,7 @@ class VirtualEnvironment(EnvironmentInterface): - PLUGIN_NAME = 'virtual' + PLUGIN_NAME = "virtual" def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) @@ -36,14 +36,14 @@ def __init__(self, *args, **kwargs): project_id if project_is_script else self.metadata.name - if 'project' in self.metadata.config - else f'{project_id}-unmanaged' + if "project" in self.metadata.config + else f"{project_id}-unmanaged" ) - venv_name = project_name if self.name == 'default' else self.name + venv_name = project_name if self.name == "default" else self.name # Conditions requiring a flat structure for build env if ( - self.isolated_data_directory == self.platform.home / '.virtualenvs' + self.isolated_data_directory == self.platform.home / ".virtualenvs" or self.root in self.isolated_data_directory.resolve().parents ): app_virtual_env_path = self.isolated_data_directory / venv_name @@ -51,7 +51,7 @@ def __init__(self, *args, **kwargs): app_virtual_env_path = self.isolated_data_directory / project_name / project_id / venv_name # Explicit path - chosen_directory = self.get_env_var_option('path') or self.config.get('path', '') + chosen_directory = self.get_env_var_option("path") or self.config.get("path", "") if chosen_directory: self.storage_path = self.data_directory / project_name / project_id self.virtual_env_path = ( @@ -61,7 +61,7 @@ def __init__(self, *args, **kwargs): self.storage_path = self.virtual_env_path = self.isolated_data_directory / venv_name # Conditions requiring a flat structure elif ( - self.data_directory == self.platform.home / '.virtualenvs' + self.data_directory == self.platform.home / ".virtualenvs" or self.root in self.data_directory.resolve().parents ): self.storage_path = self.data_directory @@ -73,7 +73,7 @@ def __init__(self, *args, **kwargs): self.virtual_env = self.virtual_env_cls(self.virtual_env_path, self.platform, self.verbosity) self.build_virtual_env = self.virtual_env_cls( - app_virtual_env_path.parent / f'{app_virtual_env_path.name}-build', self.platform, self.verbosity + app_virtual_env_path.parent / f"{app_virtual_env_path.name}-build", self.platform, self.verbosity ) self.shells = ShellManager(self) @@ -81,15 +81,15 @@ def __init__(self, *args, **kwargs): @cached_property def use_uv(self) -> bool: - return self.installer == 'uv' or bool(self.explicit_uv_path) + return self.installer == "uv" or bool(self.explicit_uv_path) @cached_property def installer(self) -> str: - return self.config.get('installer', 'pip') + return self.config.get("installer", "pip") @cached_property def explicit_uv_path(self) -> str: - return self.get_env_var_option('uv_path') or self.config.get('uv-path', '') + return self.get_env_var_option("uv_path") or self.config.get("uv-path", "") @cached_property def virtual_env_cls(self) -> type[VirtualEnv]: @@ -99,7 +99,7 @@ def expose_uv(self): if not (self.use_uv or self.uv_path): return nullcontext() - return EnvVars({'HATCH_UV': self.uv_path}) + return EnvVars({"HATCH_UV": self.uv_path}) @cached_property def uv_path(self) -> str: @@ -108,7 +108,7 @@ def uv_path(self) -> str: from hatch.env.internal import is_default_environment - env_name = 'hatch-uv' + env_name = "hatch-uv" if not ( # Prevent recursive loop self.name == env_name @@ -118,18 +118,18 @@ def uv_path(self) -> str: uv_env = self.app.project.get_environment(env_name) self.app.project.prepare_environment(uv_env) with uv_env: - return self.platform.modules.shutil.which('uv') + return self.platform.modules.shutil.which("uv") import sysconfig - scripts_dir = sysconfig.get_path('scripts') - old_path = os.environ.get('PATH', os.defpath) - new_path = f'{scripts_dir}{os.pathsep}{old_path}' - return self.platform.modules.shutil.which('uv', path=new_path) + scripts_dir = sysconfig.get_path("scripts") + old_path = os.environ.get("PATH", os.defpath) + new_path = f"{scripts_dir}{os.pathsep}{old_path}" + return self.platform.modules.shutil.which("uv", path=new_path) @staticmethod def get_option_types() -> dict: - return {'system-packages': bool, 'path': str, 'python-sources': list, 'installer': str, 'uv-path': str} + return {"system-packages": bool, "path": str, "python-sources": list, "installer": str, "uv-path": str} def activate(self): self.virtual_env.activate() @@ -144,7 +144,7 @@ def create(self): if self.root in self.storage_path.parents: # Although it would be nice to support Mercurial, only Git supports multiple ignore files. See: # https://github.com/pytest-dev/pytest/issues/3286#issuecomment-421439197 - vcs_ignore_file = self.storage_path / '.gitignore' + vcs_ignore_file = self.storage_path / ".gitignore" if not vcs_ignore_file.is_file(): vcs_ignore_file.ensure_parent_dir_exists() vcs_ignore_file.write_text( @@ -155,16 +155,16 @@ def create(self): ) with self.expose_uv(): - self.virtual_env.create(self.parent_python, allow_system_packages=self.config.get('system-packages', False)) + self.virtual_env.create(self.parent_python, allow_system_packages=self.config.get("system-packages", False)) def remove(self): self.virtual_env.remove() self.build_virtual_env.remove() # Clean up root directory of all virtual environments belonging to the project - if self.storage_path != self.platform.home / '.virtualenvs' and self.storage_path.is_dir(): + if self.storage_path != self.platform.home / ".virtualenvs" and self.storage_path.is_dir(): entries = [entry.name for entry in self.storage_path.iterdir()] - if not entries or (entries == ['.gitignore'] and self.root in self.storage_path.parents): + if not entries or (entries == [".gitignore"] and self.root in self.storage_path.parents): self.storage_path.remove() def exists(self): @@ -177,7 +177,7 @@ def install_project(self): def install_project_dev_mode(self): with self.safe_activation(): self.platform.check_command( - self.construct_pip_install_command(['--editable', self.apply_features(str(self.root))]) + self.construct_pip_install_command(["--editable", self.apply_features(str(self.root))]) ) def dependencies_in_sync(self): @@ -204,7 +204,7 @@ def construct_pip_install_command(self, args: list[str]): if not self.use_uv: return super().construct_pip_install_command(args) - command = [self.uv_path, 'pip', 'install'] + command = [self.uv_path, "pip", "install"] # Default to -1 verbosity add_verbosity_flag(command, self.verbosity, adjustment=-1) @@ -213,7 +213,7 @@ def construct_pip_install_command(self, args: list[str]): return command def enter_shell(self, name: str, path: str, args: Iterable[str]): - shell_executor = getattr(self.shells, f'enter_{name}', None) + shell_executor = getattr(self.shells, f"enter_{name}", None) if shell_executor is None: # Manually activate in lieu of an activation script with self.safe_activation(): @@ -225,7 +225,7 @@ def enter_shell(self, name: str, path: str, args: Iterable[str]): def check_compatibility(self): super().check_compatibility() - python_version = self.config.get('python', '') + python_version = self.config.get("python", "") if ( os.environ.get(AppEnvVars.PYTHON) or self._find_existing_interpreter(python_version) is not None @@ -234,23 +234,23 @@ def check_compatibility(self): return message = ( - f'cannot locate Python: {python_version}' + f"cannot locate Python: {python_version}" if python_version - else 'no compatible Python distribution available' + else "no compatible Python distribution available" ) raise OSError(message) @cached_property def _preferred_python_version(self): - return f'{sys.version_info.major}.{sys.version_info.minor}' + return f"{sys.version_info.major}.{sys.version_info.minor}" @cached_property def parent_python(self): - if python_choice := self.config.get('python', ''): + if python_choice := self.config.get("python", ""): return self._get_concrete_interpreter_path(python_choice) if explicit_default := os.environ.get(AppEnvVars.PYTHON): - return sys.executable if explicit_default == 'self' else explicit_default + return sys.executable if explicit_default == "self" else explicit_default return self._get_concrete_interpreter_path() @@ -258,7 +258,7 @@ def parent_python(self): def python_manager(self) -> PythonManager: from hatch.python.core import PythonManager - return PythonManager(self.isolated_data_directory / '.pythons') + return PythonManager(self.isolated_data_directory / ".pythons") def get_interpreter_resolver_env(self) -> dict[str, str]: env = dict(os.environ) @@ -267,19 +267,19 @@ def get_interpreter_resolver_env(self) -> dict[str, str]: return env internal_path = os.pathsep.join(python_dirs) - old_path = env.pop('PATH', None) - env['PATH'] = internal_path if old_path is None else f'{old_path}{os.pathsep}{internal_path}' + old_path = env.pop("PATH", None) + env["PATH"] = internal_path if old_path is None else f"{old_path}{os.pathsep}{internal_path}" return env def upgrade_possible_internal_python(self, python_path: str) -> None: - if 'internal' not in self._python_sources: + if "internal" not in self._python_sources: return for dist in self.python_manager.get_installed().values(): if dist.python_path == Path(python_path): if dist.needs_update(): - with self.app.status(f'Updating Python distribution: {dist.name}'): + with self.app.status(f"Updating Python distribution: {dist.name}"): self.python_manager.install(dist.name) break @@ -291,7 +291,7 @@ def _interpreter_is_compatible(self, interpreter: PythonInfo) -> bool: and (self.skip_install or self._python_constraint.contains(interpreter.version_str)) ) - def _get_concrete_interpreter_path(self, python_version: str = '') -> str | None: + def _get_concrete_interpreter_path(self, python_version: str = "") -> str | None: known_resolvers = self._python_resolvers() resolvers = [known_resolvers[source] for source in self._python_sources] if python_version: @@ -306,7 +306,7 @@ def _get_concrete_interpreter_path(self, python_version: str = '') -> str | None # Fallback to whatever is compatible for resolver in resolvers: - if (concrete_path := resolver('')) is not None: + if (concrete_path := resolver("")) is not None: return concrete_path return None @@ -320,14 +320,14 @@ def _resolve_external_interpreter_path(self, python_version: str) -> str | None: def _resolve_internal_interpreter_path(self, python_version: str) -> str | None: if (available_distribution := self._get_available_distribution(python_version)) is not None: - with self.app.status(f'Installing Python distribution: {available_distribution}'): + with self.app.status(f"Installing Python distribution: {available_distribution}"): dist = self.python_manager.install(available_distribution) return str(dist.python_path) return None - def _find_existing_interpreter(self, python_version: str = '') -> str | None: + def _find_existing_interpreter(self, python_version: str = "") -> str | None: from virtualenv.discovery import builtin as virtualenv_discovery propose_interpreters = virtualenv_discovery.propose_interpreters @@ -349,7 +349,7 @@ def _patched_propose_interpreters(*args, **kwargs): finally: virtualenv_discovery.propose_interpreters = propose_interpreters - def _get_available_distribution(self, python_version: str = '') -> str | None: + def _get_available_distribution(self, python_version: str = "") -> str | None: from hatch.python.resolve import get_compatible_distributions compatible_distributions = get_compatible_distributions() @@ -358,7 +358,7 @@ def _get_available_distribution(self, python_version: str = '') -> str | None: if not python_version: # Only try providing CPython distributions - available_distributions = [d for d in compatible_distributions if not d.startswith('pypy')] + available_distributions = [d for d in compatible_distributions if not d.startswith("pypy")] # Prioritize the version that Hatch is currently using, if available with suppress(ValueError): @@ -374,8 +374,8 @@ def _get_available_distribution(self, python_version: str = '') -> str | None: for available_distribution in available_distributions: minor_version = ( - available_distribution.replace('pypy', '', 1) - if available_distribution.startswith('pypy') + available_distribution.replace("pypy", "", 1) + if available_distribution.startswith("pypy") else available_distribution ) if not self._python_constraint.contains(minor_version): @@ -390,30 +390,30 @@ def _is_stable_path(self, executable: str) -> bool: parents = path.parents # https://pypa.github.io/pipx/how-pipx-works/ - if (Path.home() / '.local' / 'pipx' / 'venvs') in parents: + if (Path.home() / ".local" / "pipx" / "venvs") in parents: return False from platformdirs import user_data_dir # https://github.com/ofek/pyapp/blob/v0.13.0/src/app.rs#L27 - if Path(user_data_dir('pyapp', appauthor=False)) in parents: + if Path(user_data_dir("pyapp", appauthor=False)) in parents: return False # via Windows store - if self.platform.windows and str(path).endswith('WindowsApps\\python.exe'): + if self.platform.windows and str(path).endswith("WindowsApps\\python.exe"): return False # via Homebrew - return not (self.platform.macos and Path('/usr/local/Cellar') in parents) + return not (self.platform.macos and Path("/usr/local/Cellar") in parents) @cached_property def _python_sources(self) -> list[str]: - return self.config.get('python-sources') or ['external', 'internal'] + return self.config.get("python-sources") or ["external", "internal"] def _python_resolvers(self) -> dict[str, Callable[[str], str | None]]: return { - 'external': self._resolve_external_interpreter_path, - 'internal': self._resolve_internal_interpreter_path, + "external": self._resolve_external_interpreter_path, + "internal": self._resolve_internal_interpreter_path, } @cached_property @@ -423,7 +423,7 @@ def _python_constraint(self) -> SpecifierSet: # Note that we do not support this field being dynamic because if we were to set up the # build environment to retrieve the field then we would be stuck because we need to use # a satisfactory version to set up the environment - return SpecifierSet(self.metadata.config.get('project', {}).get('requires-python', '')) + return SpecifierSet(self.metadata.config.get("project", {}).get("requires-python", "")) @contextmanager def safe_activation(self): diff --git a/src/hatch/index/core.py b/src/hatch/index/core.py index c5a231bae..0f4a24086 100644 --- a/src/hatch/index/core.py +++ b/src/hatch/index/core.py @@ -16,20 +16,20 @@ def __init__(self, repo: str): self.repo = hyperlink.parse(repo).normalize() # PyPI - if self.repo.host.endswith('pypi.org'): # no cov - repo_url = self.repo.replace(host='pypi.org') if self.repo.host == 'upload.pypi.org' else self.repo + if self.repo.host.endswith("pypi.org"): # no cov + repo_url = self.repo.replace(host="pypi.org") if self.repo.host == "upload.pypi.org" else self.repo - self.simple = repo_url.click('/simple/') - self.project = repo_url.click('/project/') + self.simple = repo_url.click("/simple/") + self.project = repo_url.click("/project/") # Assume devpi else: - self.simple = self.repo.child('+simple', '') + self.simple = self.repo.child("+simple", "") self.project = self.repo class PackageIndex: - def __init__(self, repo: str, *, user='', auth='', ca_cert=None, client_cert=None, client_key=None): + def __init__(self, repo: str, *, user="", auth="", ca_cert=None, client_cert=None, client_key=None): self.urls = IndexURLs(repo) self.repo = str(self.urls.repo) self.user = user @@ -60,10 +60,10 @@ def upload_artifact(self, artifact: Path, data: dict): import hashlib import io - data[':action'] = 'file_upload' - data['protocol_version'] = '1' + data[":action"] = "file_upload" + data["protocol_version"] = "1" - with artifact.open('rb') as f: + with artifact.open("rb") as f: # https://github.com/pypa/warehouse/blob/7fc3ce5bd7ecc93ef54c1652787fb5e7757fe6f2/tests/unit/packaging/test_tasks.py#L189-L191 md5_hash = hashlib.md5() # noqa: S324 sha256_hash = hashlib.sha256() @@ -78,23 +78,23 @@ def upload_artifact(self, artifact: Path, data: dict): sha256_hash.update(chunk) blake2_256_hash.update(chunk) - data['md5_digest'] = md5_hash.hexdigest() - data['sha256_digest'] = sha256_hash.hexdigest() - data['blake2_256_digest'] = blake2_256_hash.hexdigest() + data["md5_digest"] = md5_hash.hexdigest() + data["sha256_digest"] = sha256_hash.hexdigest() + data["blake2_256_digest"] = blake2_256_hash.hexdigest() f.seek(0) response = self.client.post( self.repo, data=data, - files={'content': (artifact.name, f, 'application/octet-stream')}, + files={"content": (artifact.name, f, "application/octet-stream")}, auth=(self.user, self.auth), ) response.raise_for_status() def get_simple_api(self, project: str) -> httpx.Response: return self.client.get( - str(self.urls.simple.child(project, '')), - headers={'Cache-Control': 'no-cache'}, + str(self.urls.simple.child(project, "")), + headers={"Cache-Control": "no-cache"}, auth=(self.user, self.auth), ) diff --git a/src/hatch/index/publish.py b/src/hatch/index/publish.py index 69ad140e2..388c823d7 100644 --- a/src/hatch/index/publish.py +++ b/src/hatch/index/publish.py @@ -1,19 +1,19 @@ from hatch.index.errors import ArtifactMetadataError MULTIPLE_USE_METADATA_FIELDS = { - 'classifier', - 'dynamic', - 'license_file', - 'obsoletes_dist', - 'platform', - 'project_url', - 'provides_dist', - 'provides_extra', - 'requires_dist', - 'requires_external', - 'supported_platform', + "classifier", + "dynamic", + "license_file", + "obsoletes_dist", + "platform", + "project_url", + "provides_dist", + "provides_extra", + "requires_dist", + "requires_external", + "supported_platform", } -RENAMED_METADATA_FIELDS = {'classifier': 'classifiers', 'project_url': 'project_urls'} +RENAMED_METADATA_FIELDS = {"classifier": "classifiers", "project_url": "project_urls"} def get_wheel_form_data(artifact): @@ -21,32 +21,32 @@ def get_wheel_form_data(artifact): from packaging.tags import parse_tag - with zipfile.ZipFile(str(artifact), 'r') as zip_archive: + with zipfile.ZipFile(str(artifact), "r") as zip_archive: for path in zip_archive.namelist(): - root = path.split('/', 1)[0] - if root.endswith('.dist-info'): + root = path.split("/", 1)[0] + if root.endswith(".dist-info"): dist_info_dir = root break else: # no cov - message = f'Could not find the `.dist-info` directory in wheel: {artifact}' + message = f"Could not find the `.dist-info` directory in wheel: {artifact}" raise ArtifactMetadataError(message) try: - with zip_archive.open(f'{dist_info_dir}/METADATA') as zip_file: - metadata_file_contents = zip_file.read().decode('utf-8') + with zip_archive.open(f"{dist_info_dir}/METADATA") as zip_file: + metadata_file_contents = zip_file.read().decode("utf-8") except KeyError: # no cov - message = f'Could not find a `METADATA` file in the `{dist_info_dir}` directory' + message = f"Could not find a `METADATA` file in the `{dist_info_dir}` directory" raise ArtifactMetadataError(message) from None else: data = parse_headers(metadata_file_contents) - data['filetype'] = 'bdist_wheel' + data["filetype"] = "bdist_wheel" # Examples: # cryptography-3.4.7-pp37-pypy37_pp73-manylinux2014_x86_64.whl -> pp37 # hatchling-1rc1-py2.py3-none-any.whl -> py2.py3 - tag_component = '-'.join(artifact.stem.split('-')[-3:]) - data['pyversion'] = '.'.join(sorted({tag.interpreter for tag in parse_tag(tag_component)})) + tag_component = "-".join(artifact.stem.split("-")[-3:]) + data["pyversion"] = ".".join(sorted({tag.interpreter for tag in parse_tag(tag_component)})) return data @@ -54,29 +54,29 @@ def get_wheel_form_data(artifact): def get_sdist_form_data(artifact): import tarfile - with tarfile.open(str(artifact), 'r:gz') as tar_archive: + with tarfile.open(str(artifact), "r:gz") as tar_archive: pkg_info_dir_parts = [] for tar_info in tar_archive: if tar_info.isfile(): - pkg_info_dir_parts.append(tar_info.name.split('/', 1)[0]) + pkg_info_dir_parts.append(tar_info.name.split("/", 1)[0]) break else: # no cov - message = f'Could not find any files in sdist: {artifact}' + message = f"Could not find any files in sdist: {artifact}" raise ArtifactMetadataError(message) - pkg_info_dir_parts.append('PKG-INFO') - pkg_info_path = '/'.join(pkg_info_dir_parts) + pkg_info_dir_parts.append("PKG-INFO") + pkg_info_path = "/".join(pkg_info_dir_parts) try: with tar_archive.extractfile(pkg_info_path) as tar_file: - metadata_file_contents = tar_file.read().decode('utf-8') + metadata_file_contents = tar_file.read().decode("utf-8") except KeyError: # no cov - message = f'Could not find file: {pkg_info_path}' + message = f"Could not find file: {pkg_info_path}" raise ArtifactMetadataError(message) from None else: data = parse_headers(metadata_file_contents) - data['filetype'] = 'sdist' - data['pyversion'] = 'source' + data["filetype"] = "sdist" + data["pyversion"] = "source" return data @@ -86,10 +86,10 @@ def parse_headers(metadata_file_contents): message = email.message_from_string(metadata_file_contents) - headers = {'description': message.get_payload()} + headers = {"description": message.get_payload()} for header, value in message.items(): - normalized_header = header.lower().replace('-', '_') + normalized_header = header.lower().replace("-", "_") header_name = RENAMED_METADATA_FIELDS.get(normalized_header, normalized_header) if normalized_header in MULTIPLE_USE_METADATA_FIELDS: diff --git a/src/hatch/plugin/constants.py b/src/hatch/plugin/constants.py index fff3f545a..20d275810 100644 --- a/src/hatch/plugin/constants.py +++ b/src/hatch/plugin/constants.py @@ -1 +1 @@ -DEFAULT_CUSTOM_SCRIPT = 'hatch_plugins.py' +DEFAULT_CUSTOM_SCRIPT = "hatch_plugins.py" diff --git a/src/hatch/plugin/utils.py b/src/hatch/plugin/utils.py index 9991ef7ce..f58d430af 100644 --- a/src/hatch/plugin/utils.py +++ b/src/hatch/plugin/utils.py @@ -15,7 +15,7 @@ def load_plugin_from_script( module = module_from_spec(spec) # type: ignore[arg-type] spec.loader.exec_module(module) # type: ignore[union-attr] - plugin_finder = f'get_{plugin_id}' + plugin_finder = f"get_{plugin_id}" names = dir(module) if plugin_finder in names: return getattr(module, plugin_finder)() @@ -33,13 +33,13 @@ def load_plugin_from_script( continue if not subclasses: - message = f'Unable to find a subclass of `{plugin_class.__name__}` in `{script_name}`: {path}' + message = f"Unable to find a subclass of `{plugin_class.__name__}` in `{script_name}`: {path}" raise ValueError(message) if len(subclasses) > 1: message = ( - f'Multiple subclasses of `{plugin_class.__name__}` found in `{script_name}`, ' - f'select one by defining a function named `{plugin_finder}`: {path}' + f"Multiple subclasses of `{plugin_class.__name__}` found in `{script_name}`, " + f"select one by defining a function named `{plugin_finder}`: {path}" ) raise ValueError(message) diff --git a/src/hatch/project/config.py b/src/hatch/project/config.py index 972a2f2d6..50a461111 100644 --- a/src/hatch/project/config.py +++ b/src/hatch/project/config.py @@ -37,9 +37,9 @@ def __init__(self, root, config, plugin_manager=None): @cached_property def build(self): - config = self.config.get('build', {}) + config = self.config.get("build", {}) if not isinstance(config, dict): - message = 'Field `tool.hatch.build` must be a table' + message = "Field `tool.hatch.build` must be a table" raise TypeError(message) return BuildConfig(config) @@ -47,9 +47,9 @@ def build(self): @property def env(self): if self._env is None: - config = self.config.get('env', {}) + config = self.config.get("env", {}) if not isinstance(config, dict): - message = 'Field `tool.hatch.env` must be a table' + message = "Field `tool.hatch.env` must be a table" raise TypeError(message) self._env = config @@ -61,22 +61,22 @@ def env_requires_complex(self) -> list[Requirement]: if self._env_requires_complex is None: from packaging.requirements import InvalidRequirement, Requirement - requires = self.env.get('requires', []) + requires = self.env.get("requires", []) if not isinstance(requires, list): - message = 'Field `tool.hatch.env.requires` must be an array' + message = "Field `tool.hatch.env.requires` must be an array" raise TypeError(message) requires_complex = [] for i, entry in enumerate(requires, 1): if not isinstance(entry, str): - message = f'Requirement #{i} in `tool.hatch.env.requires` must be a string' + message = f"Requirement #{i} in `tool.hatch.env.requires` must be a string" raise TypeError(message) try: requires_complex.append(Requirement(entry)) except InvalidRequirement as e: - message = f'Requirement #{i} in `tool.hatch.env.requires` is invalid: {e}' + message = f"Requirement #{i} in `tool.hatch.env.requires` is invalid: {e}" raise ValueError(message) from None self._env_requires_complex = requires_complex @@ -93,15 +93,15 @@ def env_requires(self): @property def env_collectors(self): if self._env_collectors is None: - collectors = self.env.get('collectors', {}) + collectors = self.env.get("collectors", {}) if not isinstance(collectors, dict): - message = 'Field `tool.hatch.env.collectors` must be a table' + message = "Field `tool.hatch.env.collectors` must be a table" raise TypeError(message) - final_config = {'default': {}} + final_config = {"default": {}} for collector, config in collectors.items(): if not isinstance(config, dict): - message = f'Field `tool.hatch.env.collectors.{collector}` must be a table' + message = f"Field `tool.hatch.env.collectors.{collector}` must be a table" raise TypeError(message) final_config[collector] = config @@ -144,9 +144,9 @@ def envs(self): from hatch.utils.platform import get_platform_name if self._envs is None: - env_config = self.config.get('envs', {}) + env_config = self.config.get("envs", {}) if not isinstance(env_config, dict): - message = 'Field `tool.hatch.envs` must be a table' + message = "Field `tool.hatch.envs` must be a table" raise TypeError(message) config = {} @@ -157,7 +157,7 @@ def envs(self): if collector_class is None: from hatchling.plugin.exceptions import UnknownPluginError - message = f'Unknown environment collector: {collector}' + message = f"Unknown environment collector: {collector}" raise UnknownPluginError(message) environment_collector = collector_class(self.root, collector_config) @@ -168,7 +168,7 @@ def envs(self): for env_name, data in env_config.items(): if not isinstance(data, dict): - message = f'Field `tool.hatch.envs.{env_name}` must be a table' + message = f"Field `tool.hatch.envs.{env_name}` must be a table" raise TypeError(message) config.setdefault(env_name, {}).update(data) @@ -177,7 +177,7 @@ def envs(self): environment_collector.finalize_config(config) # Prevent plugins from removing the default environment - ensure_valid_environment(config.setdefault('default', {})) + ensure_valid_environment(config.setdefault("default", {})) seen = set() active = [] @@ -191,116 +191,116 @@ def envs(self): cached_overrides = {} for env_name, raw_initial_config in config.items(): current_cached_overrides = cached_overrides[env_name] = { - 'platform': [], - 'env': [], - 'matrix': [], - 'name': [], + "platform": [], + "env": [], + "matrix": [], + "name": [], } # Only shallow copying is necessary since we just want to modify keys initial_config = raw_initial_config.copy() - matrix_name_format = initial_config.pop('matrix-name-format', '{value}') + matrix_name_format = initial_config.pop("matrix-name-format", "{value}") if not isinstance(matrix_name_format, str): - message = f'Field `tool.hatch.envs.{env_name}.matrix-name-format` must be a string' + message = f"Field `tool.hatch.envs.{env_name}.matrix-name-format` must be a string" raise TypeError(message) - if '{value}' not in matrix_name_format: + if "{value}" not in matrix_name_format: message = ( - f'Field `tool.hatch.envs.{env_name}.matrix-name-format` must ' - f'contain at least the `{{value}}` placeholder' + f"Field `tool.hatch.envs.{env_name}.matrix-name-format` must " + f"contain at least the `{{value}}` placeholder" ) raise ValueError(message) - overrides = initial_config.pop('overrides', {}) + overrides = initial_config.pop("overrides", {}) if not isinstance(overrides, dict): - message = f'Field `tool.hatch.envs.{env_name}.overrides` must be a table' + message = f"Field `tool.hatch.envs.{env_name}.overrides` must be a table" raise TypeError(message) # Apply any configuration based on the current platform - platform_overrides = overrides.get('platform', {}) + platform_overrides = overrides.get("platform", {}) if not isinstance(platform_overrides, dict): - message = f'Field `tool.hatch.envs.{env_name}.overrides.platform` must be a table' + message = f"Field `tool.hatch.envs.{env_name}.overrides.platform` must be a table" raise TypeError(message) for platform, options in platform_overrides.items(): if not isinstance(options, dict): - message = f'Field `tool.hatch.envs.{env_name}.overrides.platform.{platform}` must be a table' + message = f"Field `tool.hatch.envs.{env_name}.overrides.platform.{platform}` must be a table" raise TypeError(message) if platform != current_platform: continue - apply_overrides(env_name, 'platform', platform, current_platform, options, initial_config) - current_cached_overrides['platform'].append((platform, current_platform, options)) + apply_overrides(env_name, "platform", platform, current_platform, options, initial_config) + current_cached_overrides["platform"].append((platform, current_platform, options)) # Apply any configuration based on environment variables - env_var_overrides = overrides.get('env', {}) + env_var_overrides = overrides.get("env", {}) if not isinstance(env_var_overrides, dict): - message = f'Field `tool.hatch.envs.{env_name}.overrides.env` must be a table' + message = f"Field `tool.hatch.envs.{env_name}.overrides.env` must be a table" raise TypeError(message) for env_var, options in env_var_overrides.items(): if not isinstance(options, dict): - message = f'Field `tool.hatch.envs.{env_name}.overrides.env.{env_var}` must be a table' + message = f"Field `tool.hatch.envs.{env_name}.overrides.env.{env_var}` must be a table" raise TypeError(message) if env_var not in environ: continue - apply_overrides(env_name, 'env', env_var, environ[env_var], options, initial_config) - current_cached_overrides['env'].append((env_var, environ[env_var], options)) + apply_overrides(env_name, "env", env_var, environ[env_var], options, initial_config) + current_cached_overrides["env"].append((env_var, environ[env_var], options)) - if 'matrix' not in initial_config: + if "matrix" not in initial_config: final_config[env_name] = initial_config continue - matrices = initial_config.pop('matrix') + matrices = initial_config.pop("matrix") if not isinstance(matrices, list): - message = f'Field `tool.hatch.envs.{env_name}.matrix` must be an array' + message = f"Field `tool.hatch.envs.{env_name}.matrix` must be an array" raise TypeError(message) - matrix_overrides = overrides.get('matrix', {}) + matrix_overrides = overrides.get("matrix", {}) if not isinstance(matrix_overrides, dict): - message = f'Field `tool.hatch.envs.{env_name}.overrides.matrix` must be a table' + message = f"Field `tool.hatch.envs.{env_name}.overrides.matrix` must be a table" raise TypeError(message) - name_overrides = overrides.get('name', {}) + name_overrides = overrides.get("name", {}) if not isinstance(name_overrides, dict): - message = f'Field `tool.hatch.envs.{env_name}.overrides.name` must be a table' + message = f"Field `tool.hatch.envs.{env_name}.overrides.name` must be a table" raise TypeError(message) - matrix_data = all_matrices[env_name] = {'config': deepcopy(initial_config)} - all_envs = matrix_data['envs'] = {} + matrix_data = all_matrices[env_name] = {"config": deepcopy(initial_config)} + all_envs = matrix_data["envs"] = {} for i, raw_matrix in enumerate(matrices, 1): matrix = raw_matrix if not isinstance(matrix, dict): - message = f'Entry #{i} in field `tool.hatch.envs.{env_name}.matrix` must be a table' + message = f"Entry #{i} in field `tool.hatch.envs.{env_name}.matrix` must be a table" raise TypeError(message) if not matrix: - message = f'Matrix #{i} in field `tool.hatch.envs.{env_name}.matrix` cannot be empty' + message = f"Matrix #{i} in field `tool.hatch.envs.{env_name}.matrix` cannot be empty" raise ValueError(message) for j, (variable, values) in enumerate(matrix.items(), 1): if not variable: message = ( - f'Variable #{j} in matrix #{i} in field `tool.hatch.envs.{env_name}.matrix` ' - f'cannot be an empty string' + f"Variable #{j} in matrix #{i} in field `tool.hatch.envs.{env_name}.matrix` " + f"cannot be an empty string" ) raise ValueError(message) if not isinstance(values, list): message = ( - f'Variable `{variable}` in matrix #{i} in field `tool.hatch.envs.{env_name}.matrix` ' - f'must be an array' + f"Variable `{variable}` in matrix #{i} in field `tool.hatch.envs.{env_name}.matrix` " + f"must be an array" ) raise TypeError(message) if not values: message = ( - f'Variable `{variable}` in matrix #{i} in field `tool.hatch.envs.{env_name}.matrix` ' - f'cannot be empty' + f"Variable `{variable}` in matrix #{i} in field `tool.hatch.envs.{env_name}.matrix` " + f"cannot be empty" ) raise ValueError(message) @@ -308,22 +308,22 @@ def envs(self): for k, value in enumerate(values, 1): if not isinstance(value, str): message = ( - f'Value #{k} of variable `{variable}` in matrix #{i} in field ' - f'`tool.hatch.envs.{env_name}.matrix` must be a string' + f"Value #{k} of variable `{variable}` in matrix #{i} in field " + f"`tool.hatch.envs.{env_name}.matrix` must be a string" ) raise TypeError(message) if not value: message = ( - f'Value #{k} of variable `{variable}` in matrix #{i} in field ' - f'`tool.hatch.envs.{env_name}.matrix` cannot be an empty string' + f"Value #{k} of variable `{variable}` in matrix #{i} in field " + f"`tool.hatch.envs.{env_name}.matrix` cannot be an empty string" ) raise ValueError(message) if value in existing_values: message = ( - f'Value #{k} of variable `{variable}` in matrix #{i} in field ' - f'`tool.hatch.envs.{env_name}.matrix` is a duplicate' + f"Value #{k} of variable `{variable}` in matrix #{i} in field " + f"`tool.hatch.envs.{env_name}.matrix` is a duplicate" ) raise ValueError(message) @@ -333,12 +333,12 @@ def envs(self): # Ensure that any Python variable comes first python_selected = False - for variable in ('py', 'python'): + for variable in ("py", "python"): if variable in matrix: if python_selected: message = ( - f'Matrix #{i} in field `tool.hatch.envs.{env_name}.matrix` ' - f'cannot contain both `py` and `python` variables' + f"Matrix #{i} in field `tool.hatch.envs.{env_name}.matrix` " + f"cannot contain both `py` and `python` variables" ) raise ValueError(message) python_selected = True @@ -362,7 +362,7 @@ def envs(self): for variable, options in matrix_overrides.items(): if not isinstance(options, dict): message = ( - f'Field `tool.hatch.envs.{env_name}.overrides.matrix.{variable}` must be a table' + f"Field `tool.hatch.envs.{env_name}.overrides.matrix.{variable}` must be a table" ) raise TypeError(message) @@ -370,50 +370,50 @@ def envs(self): continue apply_overrides( - env_name, 'matrix', variable, variable_values[variable], options, new_config + env_name, "matrix", variable, variable_values[variable], options, new_config ) cached_matrix_overrides.append((variable, variable_values[variable], options)) # Construct the environment name - final_matrix_name_format = new_config.pop('matrix-name-format', matrix_name_format) + final_matrix_name_format = new_config.pop("matrix-name-format", matrix_name_format) env_name_parts = [] for j, (variable, value) in enumerate(variable_values.items()): if j == 0 and python_selected: - new_config['python'] = value - env_name_parts.append(value if value.startswith('py') else f'py{value}') + new_config["python"] = value + env_name_parts.append(value if value.startswith("py") else f"py{value}") else: env_name_parts.append(final_matrix_name_format.format(variable=variable, value=value)) - new_env_name = '-'.join(env_name_parts) + new_env_name = "-".join(env_name_parts) cached_name_overrides = [] # Apply any configuration based on the final name, minus the prefix for non-default environments for pattern, options in name_overrides.items(): if not isinstance(options, dict): - message = f'Field `tool.hatch.envs.{env_name}.overrides.name.{pattern}` must be a table' + message = f"Field `tool.hatch.envs.{env_name}.overrides.name.{pattern}` must be a table" raise TypeError(message) if not re.search(pattern, new_env_name): continue - apply_overrides(env_name, 'name', pattern, new_env_name, options, new_config) + apply_overrides(env_name, "name", pattern, new_env_name, options, new_config) cached_name_overrides.append((pattern, new_env_name, options)) - if env_name != 'default': - new_env_name = f'{env_name}.{new_env_name}' + if env_name != "default": + new_env_name = f"{env_name}.{new_env_name}" # Save the generated environment final_config[new_env_name] = new_config cached_overrides[new_env_name] = { - 'platform': current_cached_overrides['platform'], - 'env': current_cached_overrides['env'], - 'matrix': cached_matrix_overrides, - 'name': cached_name_overrides, + "platform": current_cached_overrides["platform"], + "env": current_cached_overrides["env"], + "matrix": cached_matrix_overrides, + "name": cached_name_overrides, } all_envs[new_env_name] = variable_values - if 'py' in variable_values: - all_envs[new_env_name] = {'python': variable_values.pop('py'), **variable_values} + if "py" in variable_values: + all_envs[new_env_name] = {"python": variable_values.pop("py"), **variable_values} # Remove the root matrix generator del cached_overrides[env_name] @@ -438,7 +438,7 @@ def envs(self): # Matrix except KeyError: self._internal_matrices[internal_name] = self._matrices.pop(internal_name) - for env_name in [env_name for env_name in self._envs if env_name.startswith(f'{internal_name}.')]: + for env_name in [env_name for env_name in self._envs if env_name.startswith(f"{internal_name}.")]: self._internal_envs[env_name] = self._envs.pop(env_name) return self._envs @@ -446,14 +446,14 @@ def envs(self): @property def publish(self): if self._publish is None: - config = self.config.get('publish', {}) + config = self.config.get("publish", {}) if not isinstance(config, dict): - message = 'Field `tool.hatch.publish` must be a table' + message = "Field `tool.hatch.publish` must be a table" raise TypeError(message) for publisher, data in config.items(): if not isinstance(data, dict): - message = f'Field `tool.hatch.publish.{publisher}` must be a table' + message = f"Field `tool.hatch.publish.{publisher}` must be a table" raise TypeError(message) self._publish = config @@ -463,16 +463,16 @@ def publish(self): @property def scripts(self): if self._scripts is None: - script_config = self.config.get('scripts', {}) + script_config = self.config.get("scripts", {}) if not isinstance(script_config, dict): - message = 'Field `tool.hatch.scripts` must be a table' + message = "Field `tool.hatch.scripts` must be a table" raise TypeError(message) config = {} for name, data in script_config.items(): - if ' ' in name: - message = f'Script name `{name}` in field `tool.hatch.scripts` must not contain spaces' + if " " in name: + message = f"Script name `{name}` in field `tool.hatch.scripts` must not contain spaces" raise ValueError(message) commands = [] @@ -482,12 +482,12 @@ def scripts(self): elif isinstance(data, list): for i, command in enumerate(data, 1): if not isinstance(command, str): - message = f'Command #{i} in field `tool.hatch.scripts.{name}` must be a string' + message = f"Command #{i} in field `tool.hatch.scripts.{name}` must be a string" raise TypeError(message) commands.append(command) else: - message = f'Field `tool.hatch.scripts.{name}` must be a string or an array of strings' + message = f"Field `tool.hatch.scripts.{name}` must be a string or an array of strings" raise TypeError(message) config[name] = commands @@ -525,46 +525,46 @@ def __init__(self, config: dict[str, Any]) -> None: @cached_property def directory(self) -> str: - directory = self.__config.get('directory', DEFAULT_BUILD_DIRECTORY) + directory = self.__config.get("directory", DEFAULT_BUILD_DIRECTORY) if not isinstance(directory, str): - message = 'Field `tool.hatch.build.directory` must be a string' + message = "Field `tool.hatch.build.directory` must be a string" raise TypeError(message) return directory @cached_property def dependencies(self) -> list[str]: - dependencies: list[str] = self.__config.get('dependencies', []) + dependencies: list[str] = self.__config.get("dependencies", []) if not isinstance(dependencies, list): - message = 'Field `tool.hatch.build.dependencies` must be an array' + message = "Field `tool.hatch.build.dependencies` must be an array" raise TypeError(message) for i, dependency in enumerate(dependencies, 1): if not isinstance(dependency, str): - message = f'Dependency #{i} in field `tool.hatch.build.dependencies` must be a string' + message = f"Dependency #{i} in field `tool.hatch.build.dependencies` must be a string" raise TypeError(message) return list(dependencies) @cached_property def hook_config(self) -> dict[str, dict[str, Any]]: - hook_config: dict[str, dict[str, Any]] = self.__config.get('hooks', {}) + hook_config: dict[str, dict[str, Any]] = self.__config.get("hooks", {}) if not isinstance(hook_config, dict): - message = 'Field `tool.hatch.build.hooks` must be a table' + message = "Field `tool.hatch.build.hooks` must be a table" raise TypeError(message) for hook_name, config in hook_config.items(): if not isinstance(config, dict): - message = f'Field `tool.hatch.build.hooks.{hook_name}` must be a table' + message = f"Field `tool.hatch.build.hooks.{hook_name}` must be a table" raise TypeError(message) return finalize_hook_config(hook_config) @cached_property def __target_config(self) -> dict[str, Any]: - config = self.__config.get('targets', {}) + config = self.__config.get("targets", {}) if not isinstance(config, dict): - message = 'Field `tool.hatch.build.targets` must be a table' + message = "Field `tool.hatch.build.targets` must be a table" raise TypeError(message) return config @@ -575,7 +575,7 @@ def target(self, target: str) -> BuildTargetConfig: config = self.__target_config.get(target, {}) if not isinstance(config, dict): - message = f'Field `tool.hatch.build.targets.{target}` must be a table' + message = f"Field `tool.hatch.build.targets.{target}` must be a table" raise TypeError(message) target_config = BuildTargetConfig(target, config, self) @@ -591,24 +591,24 @@ def __init__(self, name: str, config: dict[str, Any], global_config: BuildConfig @cached_property def directory(self) -> str: - directory = self.__config.get('directory', self.__global_config.directory) + directory = self.__config.get("directory", self.__global_config.directory) if not isinstance(directory, str): - message = f'Field `tool.hatch.build.targets.{self.__name}.directory` must be a string' + message = f"Field `tool.hatch.build.targets.{self.__name}.directory` must be a string" raise TypeError(message) return directory @cached_property def dependencies(self) -> list[str]: - dependencies: list[str] = self.__config.get('dependencies', []) + dependencies: list[str] = self.__config.get("dependencies", []) if not isinstance(dependencies, list): - message = f'Field `tool.hatch.build.targets.{self.__name}.dependencies` must be an array' + message = f"Field `tool.hatch.build.targets.{self.__name}.dependencies` must be an array" raise TypeError(message) for i, dependency in enumerate(dependencies, 1): if not isinstance(dependency, str): message = ( - f'Dependency #{i} in field `tool.hatch.build.targets.{self.__name}.dependencies` must be a string' + f"Dependency #{i} in field `tool.hatch.build.targets.{self.__name}.dependencies` must be a string" ) raise TypeError(message) @@ -618,14 +618,14 @@ def dependencies(self) -> list[str]: @cached_property def hook_config(self) -> dict[str, dict[str, Any]]: - hook_config: dict[str, dict[str, Any]] = self.__config.get('hooks', {}) + hook_config: dict[str, dict[str, Any]] = self.__config.get("hooks", {}) if not isinstance(hook_config, dict): - message = f'Field `tool.hatch.build.targets.{self.__name}.hooks` must be a table' + message = f"Field `tool.hatch.build.targets.{self.__name}.hooks` must be a table" raise TypeError(message) for hook_name, config in hook_config.items(): if not isinstance(config, dict): - message = f'Field `tool.hatch.build.targets.{self.__name}.hooks.{hook_name}` must be a table' + message = f"Field `tool.hatch.build.targets.{self.__name}.hooks.{hook_name}` must be a table" raise TypeError(message) config = self.__global_config.hook_config.copy() @@ -640,7 +640,7 @@ def expand_script_commands(script_name, commands, config, seen, active): if script_name in active: active.append(script_name) - message = f'Circular expansion detected for field `tool.hatch.scripts`: {" -> ".join(active)}' + message = f"Circular expansion detected for field `tool.hatch.scripts`: {' -> '.join(active)}" raise ValueError(message) active.append(script_name) @@ -671,19 +671,19 @@ def _populate_default_env_values(env_name, data, config, seen, active): if env_name in seen: return - if data.pop('detached', False): - data['template'] = env_name - data['skip-install'] = True + if data.pop("detached", False): + data["template"] = env_name + data["skip-install"] = True - template_name = data.pop('template', 'default') + template_name = data.pop("template", "default") if template_name not in config: - message = f'Field `tool.hatch.envs.{env_name}.template` refers to an unknown environment `{template_name}`' + message = f"Field `tool.hatch.envs.{env_name}.template` refers to an unknown environment `{template_name}`" raise ValueError(message) if env_name in active: active.append(env_name) - message = f'Circular inheritance detected for field `tool.hatch.envs.*.template`: {" -> ".join(active)}' + message = f"Circular inheritance detected for field `tool.hatch.envs.*.template`: {' -> '.join(active)}" raise ValueError(message) if template_name == env_name: @@ -697,11 +697,11 @@ def _populate_default_env_values(env_name, data, config, seen, active): _populate_default_env_values(template_name, template_config, config, seen, active) for key, value in template_config.items(): - if key == 'matrix': + if key == "matrix": continue - if key == 'scripts': - scripts = data['scripts'] if 'scripts' in data else data.setdefault('scripts', {}) + if key == "scripts": + scripts = data["scripts"] if "scripts" in data else data.setdefault("scripts", {}) for script, commands in value.items(): scripts.setdefault(script, commands) else: @@ -721,8 +721,8 @@ def finalize_hook_config(hook_config: dict[str, dict[str, Any]]) -> dict[str, di for hook_name, config in hook_config.items() if ( all_hooks_enabled - or config.get('enable-by-default', True) - or env_var_enabled(f'{BuildEnvVars.HOOK_ENABLE_PREFIX}{hook_name.upper()}') + or config.get("enable-by-default", True) + or env_var_enabled(f"{BuildEnvVars.HOOK_ENABLE_PREFIX}{hook_name.upper()}") ) } return final_hook_config @@ -730,6 +730,6 @@ def finalize_hook_config(hook_config: dict[str, dict[str, Any]]) -> dict[str, di def env_var_enabled(env_var: str, *, default: bool = False) -> bool: if env_var in environ: - return environ[env_var] in {'1', 'true'} + return environ[env_var] in {"1", "true"} return default diff --git a/src/hatch/project/constants.py b/src/hatch/project/constants.py index 6da1f339a..27315cdfd 100644 --- a/src/hatch/project/constants.py +++ b/src/hatch/project/constants.py @@ -1,14 +1,14 @@ -BUILD_BACKEND = 'hatchling.build' -DEFAULT_BUILD_DIRECTORY = 'dist' -DEFAULT_BUILD_SCRIPT = 'hatch_build.py' -DEFAULT_CONFIG_FILE = 'hatch.toml' +BUILD_BACKEND = "hatchling.build" +DEFAULT_BUILD_DIRECTORY = "dist" +DEFAULT_BUILD_SCRIPT = "hatch_build.py" +DEFAULT_CONFIG_FILE = "hatch.toml" class BuildEnvVars: - LOCATION = 'HATCH_BUILD_LOCATION' - HOOKS_ONLY = 'HATCH_BUILD_HOOKS_ONLY' - NO_HOOKS = 'HATCH_BUILD_NO_HOOKS' - HOOKS_ENABLE = 'HATCH_BUILD_HOOKS_ENABLE' - HOOK_ENABLE_PREFIX = 'HATCH_BUILD_HOOK_ENABLE_' - CLEAN = 'HATCH_BUILD_CLEAN' - CLEAN_HOOKS_AFTER = 'HATCH_BUILD_CLEAN_HOOKS_AFTER' + LOCATION = "HATCH_BUILD_LOCATION" + HOOKS_ONLY = "HATCH_BUILD_HOOKS_ONLY" + NO_HOOKS = "HATCH_BUILD_NO_HOOKS" + HOOKS_ENABLE = "HATCH_BUILD_HOOKS_ENABLE" + HOOK_ENABLE_PREFIX = "HATCH_BUILD_HOOK_ENABLE_" + CLEAN = "HATCH_BUILD_CLEAN" + CLEAN_HOOKS_AFTER = "HATCH_BUILD_CLEAN_HOOKS_AFTER" diff --git a/src/hatch/project/core.py b/src/hatch/project/core.py index 8e7392fc2..c461a79b8 100644 --- a/src/hatch/project/core.py +++ b/src/hatch/project/core.py @@ -79,7 +79,7 @@ def set_app(self, app: Application) -> None: @cached_property def app(self) -> Application: if self.__app is None: # no cov - message = 'The application has not been set' + message = "The application has not been set" raise RuntimeError(message) from hatch.cli.application import Application @@ -89,9 +89,9 @@ def app(self) -> Application: @cached_property def build_env(self) -> EnvironmentInterface: # Prevent the default environment from being used as a builder environment - environment = self.get_environment('hatch-build' if self.app.env == 'default' else self.app.env) + environment = self.get_environment("hatch-build" if self.app.env == "default" else self.app.env) if not environment.builder: - self.app.abort(f'Environment `{environment.name}` is not a builder environment') + self.app.abort(f"Environment `{environment.name}` is not a builder environment") return environment @@ -103,7 +103,7 @@ def build_frontend(self) -> BuildFrontend: @cached_property def env_metadata(self) -> EnvironmentMetadata: - return EnvironmentMetadata(self.app.data_dir / 'env' / '.metadata', self.location) + return EnvironmentMetadata(self.app.data_dir / "env" / ".metadata", self.location) def get_environment(self, env_name: str | None = None) -> EnvironmentInterface: if env_name is None: @@ -114,22 +114,22 @@ def get_environment(self, env_name: str | None = None) -> EnvironmentInterface: elif env_name in self.config.envs: config = self.config.envs[env_name] else: - self.app.abort(f'Unknown environment: {env_name}') + self.app.abort(f"Unknown environment: {env_name}") - environment_type = config['type'] + environment_type = config["type"] environment_class = self.plugin_manager.environment.get(environment_type) if environment_class is None: - self.app.abort(f'Environment `{env_name}` has unknown type: {environment_type}') + self.app.abort(f"Environment `{env_name}` has unknown type: {environment_type}") from hatch.env.internal import is_isolated_environment if self.location.is_file(): - data_directory = isolated_data_directory = self.app.data_dir / 'env' / environment_type / '.scripts' + data_directory = isolated_data_directory = self.app.data_dir / "env" / environment_type / ".scripts" elif is_isolated_environment(env_name, config): - data_directory = isolated_data_directory = self.app.data_dir / 'env' / '.internal' / env_name + data_directory = isolated_data_directory = self.app.data_dir / "env" / ".internal" / env_name else: data_directory = self.app.get_env_directory(environment_type) - isolated_data_directory = self.app.data_dir / 'env' / environment_type + isolated_data_directory = self.app.data_dir / "env" / environment_type self.config.finalize_env_overrides(environment_class.get_option_types()) @@ -162,7 +162,7 @@ def prepare_environment(self, environment: EnvironmentInterface): ExecutionContext( environment, shell_commands=environment.pre_install_commands, - source='pre-install', + source="pre-install", show_code_on_error=True, ) ) @@ -179,7 +179,7 @@ def prepare_environment(self, environment: EnvironmentInterface): ExecutionContext( environment, shell_commands=environment.post_install_commands, - source='post-install', + source="post-install", show_code_on_error=True, ) ) @@ -203,7 +203,7 @@ def prepare_build_environment(self, *, targets: list[str] | None = None) -> None from hatch.project.constants import BUILD_BACKEND if targets is None: - targets = ['wheel'] + targets = ["wheel"] build_backend = self.metadata.build.build_backend with self.location.as_cwd(), self.build_env.get_env_vars(): @@ -211,20 +211,20 @@ def prepare_build_environment(self, *, targets: list[str] | None = None) -> None try: self.build_env.check_compatibility() except Exception as e: # noqa: BLE001 - self.app.abort(f'Environment `{self.build_env.name}` is incompatible: {e}') + self.app.abort(f"Environment `{self.build_env.name}` is incompatible: {e}") self.prepare_environment(self.build_env) extra_dependencies: list[str] = [] - with self.app.status('Inspecting build dependencies'): + with self.app.status("Inspecting build dependencies"): if build_backend != BUILD_BACKEND: for target in targets: - if target == 'sdist': - extra_dependencies.extend(self.build_frontend.get_requires('sdist')) - elif target == 'wheel': - extra_dependencies.extend(self.build_frontend.get_requires('wheel')) + if target == "sdist": + extra_dependencies.extend(self.build_frontend.get_requires("sdist")) + elif target == "wheel": + extra_dependencies.extend(self.build_frontend.get_requires("wheel")) else: - self.app.abort(f'Target `{target}` is not supported by `{build_backend}`') + self.app.abort(f"Target `{target}` is not supported by `{build_backend}`") else: required_build_deps = self.build_frontend.hatch.get_required_build_deps(targets) if required_build_deps: @@ -237,10 +237,10 @@ def prepare_build_environment(self, *, targets: list[str] | None = None) -> None self.build_env.sync_dependencies() def get_dependencies(self) -> tuple[list[str], dict[str, list[str]]]: - dynamic_fields = {'dependencies', 'optional-dependencies'} + dynamic_fields = {"dependencies", "optional-dependencies"} if not dynamic_fields.intersection(self.metadata.dynamic): - dependencies: list[str] = self.metadata.core_raw_metadata.get('dependencies', []) - features: dict[str, list[str]] = self.metadata.core_raw_metadata.get('optional-dependencies', {}) + dependencies: list[str] = self.metadata.core_raw_metadata.get("dependencies", []) + features: dict[str, list[str]] = self.metadata.core_raw_metadata.get("optional-dependencies", {}) return dependencies, features from hatch.project.constants import BUILD_BACKEND @@ -253,17 +253,17 @@ def get_dependencies(self) -> tuple[list[str], dict[str, list[str]]]: else: project_metadata = self.build_frontend.hatch.get_core_metadata() - dynamic_dependencies: list[str] = project_metadata.get('dependencies', []) - dynamic_features: dict[str, list[str]] = project_metadata.get('optional-dependencies', {}) + dynamic_dependencies: list[str] = project_metadata.get("dependencies", []) + dynamic_features: dict[str, list[str]] = project_metadata.get("optional-dependencies", {}) return dynamic_dependencies, dynamic_features def expand_environments(self, env_name: str) -> list[str]: if env_name in self.config.internal_matrices: - return list(self.config.internal_matrices[env_name]['envs']) + return list(self.config.internal_matrices[env_name]["envs"]) if env_name in self.config.matrices: - return list(self.config.matrices[env_name]['envs']) + return list(self.config.matrices[env_name]["envs"]) if env_name in self.config.internal_envs: return [env_name] @@ -298,12 +298,12 @@ def find_project_root(self) -> Path | None: path = self._path while True: - possible_file = path.joinpath('pyproject.toml') + possible_file = path.joinpath("pyproject.toml") if possible_file.is_file(): self._project_file_path = possible_file return path - if path.joinpath('setup.py').is_file(): + if path.joinpath("setup.py").is_file(): return path new_path = path.parent @@ -325,10 +325,10 @@ def ensure_cwd(self) -> Generator[Path, None, None]: @staticmethod def canonicalize_name(name: str, *, strict=True) -> str: if strict: - return re.sub(r'[-_.]+', '-', name).lower() + return re.sub(r"[-_.]+", "-", name).lower() # Used for creating new projects - return re.sub(r'[-_. ]+', '-', name).lower() + return re.sub(r"[-_. ]+", "-", name).lower() @property def metadata(self): @@ -344,14 +344,14 @@ def raw_config(self): if self._raw_config is None: if self.root is None or self._project_file_path is None: # Assume no pyproject.toml e.g. environment management only - self._raw_config = {'project': {'name': self.location.name}} + self._raw_config = {"project": {"name": self.location.name}} else: from hatch.utils.toml import load_toml_file raw_config = load_toml_file(str(self._project_file_path)) # Assume environment management only - if 'project' not in raw_config: - raw_config['project'] = {'name': self.location.name} + if "project" not in raw_config: + raw_config["project"] = {"name": self.location.name} self._raw_config = raw_config @@ -360,50 +360,50 @@ def raw_config(self): def save_config(self, config): import tomlkit - with open(str(self._project_file_path), 'w', encoding='utf-8') as f: + with open(str(self._project_file_path), "w", encoding="utf-8") as f: f.write(tomlkit.dumps(config)) @staticmethod def initialize(project_file_path, template_config): import tomlkit - with open(str(project_file_path), encoding='utf-8') as f: + with open(str(project_file_path), encoding="utf-8") as f: raw_config = tomlkit.parse(f.read()) - build_system_config = raw_config.setdefault('build-system', {}) + build_system_config = raw_config.setdefault("build-system", {}) build_system_config.clear() - build_system_config['requires'] = ['hatchling'] - build_system_config['build-backend'] = 'hatchling.build' + build_system_config["requires"] = ["hatchling"] + build_system_config["build-backend"] = "hatchling.build" - project_config = raw_config.get('project') + project_config = raw_config.get("project") if project_config is None: - raw_config['project'] = project_config = {} + raw_config["project"] = project_config = {} - project_name = project_config.get('name') + project_name = project_config.get("name") if not project_name: - project_config['name'] = template_config['project_name_normalized'] + project_config["name"] = template_config["project_name_normalized"] - project_description = project_config.get('description') + project_description = project_config.get("description") if not project_description: - project_config['description'] = template_config['description'] + project_config["description"] = template_config["description"] - project_config['dynamic'] = ['version'] + project_config["dynamic"] = ["version"] - tool_config = raw_config.get('tool') + tool_config = raw_config.get("tool") if tool_config is None: - raw_config['tool'] = tool_config = {} + raw_config["tool"] = tool_config = {} - hatch_config = tool_config.get('hatch') + hatch_config = tool_config.get("hatch") if hatch_config is None: - tool_config['hatch'] = hatch_config = {} + tool_config["hatch"] = hatch_config = {} - version_config = hatch_config.get('version') + version_config = hatch_config.get("version") if version_config is None: - hatch_config['version'] = version_config = {} + hatch_config["version"] = version_config = {} version_config.clear() - version_config['path'] = f'{template_config["package_name"]}/__init__.py' + version_config["path"] = f"{template_config['package_name']}/__init__.py" - with open(str(project_file_path), 'w', encoding='utf-8') as f: + with open(str(project_file_path), "w", encoding="utf-8") as f: f.write(tomlkit.dumps(raw_config)) diff --git a/src/hatch/project/env.py b/src/hatch/project/env.py index 27420cc51..2d9a40115 100644 --- a/src/hatch/project/env.py +++ b/src/hatch/project/env.py @@ -11,22 +11,22 @@ from hatch.utils.fs import Path RESERVED_OPTIONS = { - 'builder': bool, - 'dependencies': list, - 'extra-dependencies': list, - 'dev-mode': bool, - 'env-exclude': list, - 'env-include': list, - 'env-vars': dict, - 'features': list, - 'matrix-name-format': str, - 'platforms': list, - 'post-install-commands': list, - 'pre-install-commands': list, - 'python': str, - 'scripts': dict, - 'skip-install': bool, - 'type': str, + "builder": bool, + "dependencies": list, + "extra-dependencies": list, + "dev-mode": bool, + "env-exclude": list, + "env-include": list, + "env-vars": dict, + "features": list, + "matrix-name-format": str, + "platforms": list, + "post-install-commands": list, + "pre-install-commands": list, + "python": str, + "scripts": dict, + "skip-install": bool, + "type": str, } @@ -35,7 +35,7 @@ def apply_overrides(env_name, source, condition, condition_value, options, new_c option_types = RESERVED_OPTIONS for raw_option, data in options.items(): - _, separator, option = raw_option.rpartition('set-') + _, separator, option = raw_option.rpartition("set-") overwrite = bool(separator) # Prevent manipulation of reserved options @@ -47,13 +47,13 @@ def apply_overrides(env_name, source, condition, condition_value, options, new_c TYPE_OVERRIDES[override_type]( env_name, option, data, source, condition, condition_value, new_config, overwrite ) - elif isinstance(data, dict) and 'value' in data: + elif isinstance(data, dict) and "value" in data: if _resolve_condition(env_name, option, source, condition, condition_value, data): - new_config[option] = data['value'] + new_config[option] = data["value"] elif option_types is not RESERVED_OPTIONS: message = ( - f'Untyped option `tool.hatch.envs.{env_name}.overrides.{source}.{condition}.{option}` ' - f'must be defined as a table with a `value` key' + f"Untyped option `tool.hatch.envs.{env_name}.overrides.{source}.{condition}.{option}` " + f"must be defined as a table with a `value` key" ) raise ValueError(message) @@ -61,57 +61,57 @@ def apply_overrides(env_name, source, condition, condition_value, options, new_c def _apply_override_to_mapping(env_name, option, data, source, condition, condition_value, new_config, overwrite): new_mapping = {} if isinstance(data, str): - key, separator, value = data.partition('=') + key, separator, value = data.partition("=") if not separator: value = condition_value new_mapping[key] = value elif isinstance(data, list): for i, entry in enumerate(data, 1): if isinstance(entry, str): - key, separator, value = entry.partition('=') + key, separator, value = entry.partition("=") if not separator: value = condition_value new_mapping[key] = value elif isinstance(entry, dict): - if 'key' not in entry: + if "key" not in entry: message = ( - f'Entry #{i} in field `tool.hatch.envs.{env_name}.overrides.{source}.{condition}.{option}` ' - f'must have an option named `key`' + f"Entry #{i} in field `tool.hatch.envs.{env_name}.overrides.{source}.{condition}.{option}` " + f"must have an option named `key`" ) raise ValueError(message) - key = entry['key'] + key = entry["key"] if not isinstance(key, str): message = ( - f'Option `key` in entry #{i} in field `tool.hatch.envs.{env_name}.overrides.{source}.' - f'{condition}.{option}` must be a string' + f"Option `key` in entry #{i} in field `tool.hatch.envs.{env_name}.overrides.{source}." + f"{condition}.{option}` must be a string" ) raise TypeError(message) if not key: message = ( - f'Option `key` in entry #{i} in field `tool.hatch.envs.{env_name}.overrides.{source}.' - f'{condition}.{option}` cannot be an empty string' + f"Option `key` in entry #{i} in field `tool.hatch.envs.{env_name}.overrides.{source}." + f"{condition}.{option}` cannot be an empty string" ) raise ValueError(message) - value = entry.get('value', condition_value) + value = entry.get("value", condition_value) if not isinstance(value, str): message = ( - f'Option `value` in entry #{i} in field `tool.hatch.envs.{env_name}.overrides.{source}.' - f'{condition}.{option}` must be a string' + f"Option `value` in entry #{i} in field `tool.hatch.envs.{env_name}.overrides.{source}." + f"{condition}.{option}` must be a string" ) raise TypeError(message) if _resolve_condition(env_name, option, source, condition, condition_value, entry, i): new_mapping[key] = value else: message = ( - f'Entry #{i} in field `tool.hatch.envs.{env_name}.overrides.{source}.{condition}.{option}` ' - f'must be a string or an inline table' + f"Entry #{i} in field `tool.hatch.envs.{env_name}.overrides.{source}.{condition}.{option}` " + f"must be a string or an inline table" ) raise TypeError(message) else: message = ( - f'Field `tool.hatch.envs.{env_name}.overrides.{source}.{condition}.{option}` must be a string or an array' + f"Field `tool.hatch.envs.{env_name}.overrides.{source}.{condition}.{option}` must be a string or an array" ) raise TypeError(message) @@ -125,7 +125,7 @@ def _apply_override_to_mapping(env_name, option, data, source, condition, condit def _apply_override_to_array(env_name, option, data, source, condition, condition_value, new_config, overwrite): if not isinstance(data, list): - message = f'Field `tool.hatch.envs.{env_name}.overrides.{source}.{condition}.{option}` must be an array' + message = f"Field `tool.hatch.envs.{env_name}.overrides.{source}.{condition}.{option}` must be an array" raise TypeError(message) new_array = [] @@ -133,32 +133,32 @@ def _apply_override_to_array(env_name, option, data, source, condition, conditio if isinstance(entry, str): new_array.append(entry) elif isinstance(entry, dict): - if 'value' not in entry: + if "value" not in entry: message = ( - f'Entry #{i} in field `tool.hatch.envs.{env_name}.overrides.{source}.{condition}.{option}` ' - f'must have an option named `value`' + f"Entry #{i} in field `tool.hatch.envs.{env_name}.overrides.{source}.{condition}.{option}` " + f"must have an option named `value`" ) raise ValueError(message) - value = entry['value'] + value = entry["value"] if not isinstance(value, str): message = ( - f'Option `value` in entry #{i} in field `tool.hatch.envs.{env_name}.overrides.{source}.' - f'{condition}.{option}` must be a string' + f"Option `value` in entry #{i} in field `tool.hatch.envs.{env_name}.overrides.{source}." + f"{condition}.{option}` must be a string" ) raise TypeError(message) if not value: message = ( - f'Option `value` in entry #{i} in field `tool.hatch.envs.{env_name}.overrides.{source}.' - f'{condition}.{option}` cannot be an empty string' + f"Option `value` in entry #{i} in field `tool.hatch.envs.{env_name}.overrides.{source}." + f"{condition}.{option}` cannot be an empty string" ) raise ValueError(message) if _resolve_condition(env_name, option, source, condition, condition_value, entry, i): new_array.append(value) else: message = ( - f'Entry #{i} in field `tool.hatch.envs.{env_name}.overrides.{source}.{condition}.{option}` ' - f'must be a string or an inline table' + f"Entry #{i} in field `tool.hatch.envs.{env_name}.overrides.{source}.{condition}.{option}` " + f"must be a string or an inline table" ) raise TypeError(message) @@ -183,17 +183,17 @@ def _apply_override_to_string( if isinstance(data, str): new_config[option] = data elif isinstance(data, dict): - if 'value' not in data: + if "value" not in data: message = ( - f'Field `tool.hatch.envs.{env_name}.overrides.{source}.{condition}.{option}` ' - f'must have an option named `value`' + f"Field `tool.hatch.envs.{env_name}.overrides.{source}.{condition}.{option}` " + f"must have an option named `value`" ) raise ValueError(message) - value = data['value'] + value = data["value"] if not isinstance(value, str): message = ( - f'Option `value` in field `tool.hatch.envs.{env_name}.overrides.{source}.' - f'{condition}.{option}` must be a string' + f"Option `value` in field `tool.hatch.envs.{env_name}.overrides.{source}." + f"{condition}.{option}` must be a string" ) raise TypeError(message) if _resolve_condition(env_name, option, source, condition, condition_value, data): @@ -205,17 +205,17 @@ def _apply_override_to_string( break if isinstance(entry, dict): - if 'value' not in entry: + if "value" not in entry: message = ( - f'Entry #{i} in field `tool.hatch.envs.{env_name}.overrides.{source}.{condition}.{option}` ' - f'must have an option named `value`' + f"Entry #{i} in field `tool.hatch.envs.{env_name}.overrides.{source}.{condition}.{option}` " + f"must have an option named `value`" ) raise ValueError(message) - value = entry['value'] + value = entry["value"] if not isinstance(value, str): message = ( - f'Option `value` in entry #{i} in field `tool.hatch.envs.{env_name}.overrides.{source}.' - f'{condition}.{option}` must be a string' + f"Option `value` in entry #{i} in field `tool.hatch.envs.{env_name}.overrides.{source}." + f"{condition}.{option}` must be a string" ) raise TypeError(message) if _resolve_condition(env_name, option, source, condition, condition_value, entry, i): @@ -223,14 +223,14 @@ def _apply_override_to_string( break else: message = ( - f'Entry #{i} in field `tool.hatch.envs.{env_name}.overrides.{source}.{condition}.{option}` ' - f'must be a string or an inline table' + f"Entry #{i} in field `tool.hatch.envs.{env_name}.overrides.{source}.{condition}.{option}` " + f"must be a string or an inline table" ) raise TypeError(message) else: message = ( - f'Field `tool.hatch.envs.{env_name}.overrides.{source}.{condition}.{option}` ' - f'must be a string, inline table, or an array' + f"Field `tool.hatch.envs.{env_name}.overrides.{source}.{condition}.{option}` " + f"must be a string, inline table, or an array" ) raise TypeError(message) @@ -248,17 +248,17 @@ def _apply_override_to_boolean( if isinstance(data, bool): new_config[option] = data elif isinstance(data, dict): - if 'value' not in data: + if "value" not in data: message = ( - f'Field `tool.hatch.envs.{env_name}.overrides.{source}.{condition}.{option}` ' - f'must have an option named `value`' + f"Field `tool.hatch.envs.{env_name}.overrides.{source}.{condition}.{option}` " + f"must have an option named `value`" ) raise ValueError(message) - value = data['value'] + value = data["value"] if not isinstance(value, bool): message = ( - f'Option `value` in field `tool.hatch.envs.{env_name}.overrides.{source}.' - f'{condition}.{option}` must be a boolean' + f"Option `value` in field `tool.hatch.envs.{env_name}.overrides.{source}." + f"{condition}.{option}` must be a boolean" ) raise TypeError(message) if _resolve_condition(env_name, option, source, condition, condition_value, data): @@ -270,17 +270,17 @@ def _apply_override_to_boolean( break if isinstance(entry, dict): - if 'value' not in entry: + if "value" not in entry: message = ( - f'Entry #{i} in field `tool.hatch.envs.{env_name}.overrides.{source}.{condition}.{option}` ' - f'must have an option named `value`' + f"Entry #{i} in field `tool.hatch.envs.{env_name}.overrides.{source}.{condition}.{option}` " + f"must have an option named `value`" ) raise ValueError(message) - value = entry['value'] + value = entry["value"] if not isinstance(value, bool): message = ( - f'Option `value` in entry #{i} in field `tool.hatch.envs.{env_name}.overrides.{source}.' - f'{condition}.{option}` must be a boolean' + f"Option `value` in entry #{i} in field `tool.hatch.envs.{env_name}.overrides.{source}." + f"{condition}.{option}` must be a boolean" ) raise TypeError(message) if _resolve_condition(env_name, option, source, condition, condition_value, entry, i): @@ -288,59 +288,59 @@ def _apply_override_to_boolean( break else: message = ( - f'Entry #{i} in field `tool.hatch.envs.{env_name}.overrides.{source}.{condition}.{option}` ' - f'must be a boolean or an inline table' + f"Entry #{i} in field `tool.hatch.envs.{env_name}.overrides.{source}.{condition}.{option}` " + f"must be a boolean or an inline table" ) raise TypeError(message) else: message = ( - f'Field `tool.hatch.envs.{env_name}.overrides.{source}.{condition}.{option}` ' - f'must be a boolean, inline table, or an array' + f"Field `tool.hatch.envs.{env_name}.overrides.{source}.{condition}.{option}` " + f"must be a boolean, inline table, or an array" ) raise TypeError(message) def _resolve_condition(env_name, option, source, condition, condition_value, condition_config, condition_index=None): - location = 'field' if condition_index is None else f'entry #{condition_index} in field' + location = "field" if condition_index is None else f"entry #{condition_index} in field" - if 'if' in condition_config: - allowed_values = condition_config['if'] + if "if" in condition_config: + allowed_values = condition_config["if"] if not isinstance(allowed_values, list): message = ( - f'Option `if` in {location} `tool.hatch.envs.{env_name}.overrides.{source}.' - f'{condition}.{option}` must be an array' + f"Option `if` in {location} `tool.hatch.envs.{env_name}.overrides.{source}." + f"{condition}.{option}` must be an array" ) raise TypeError(message) if condition_value not in allowed_values: return False - if 'platform' in condition_config: - allowed_platforms = condition_config['platform'] + if "platform" in condition_config: + allowed_platforms = condition_config["platform"] if not isinstance(allowed_platforms, list): message = ( - f'Option `platform` in {location} `tool.hatch.envs.{env_name}.overrides.{source}.' - f'{condition}.{option}` must be an array' + f"Option `platform` in {location} `tool.hatch.envs.{env_name}.overrides.{source}." + f"{condition}.{option}` must be an array" ) raise TypeError(message) for i, entry in enumerate(allowed_platforms, 1): if not isinstance(entry, str): message = ( - f'Item #{i} in option `platform` in {location} `tool.hatch.envs.{env_name}.overrides.{source}.' - f'{condition}.{option}` must be a string' + f"Item #{i} in option `platform` in {location} `tool.hatch.envs.{env_name}.overrides.{source}." + f"{condition}.{option}` must be a string" ) raise TypeError(message) if get_platform_name() not in allowed_platforms: return False - if 'env' in condition_config: - env_vars = condition_config['env'] + if "env" in condition_config: + env_vars = condition_config["env"] if not isinstance(env_vars, list): message = ( - f'Option `env` in {location} `tool.hatch.envs.{env_name}.overrides.{source}.' - f'{condition}.{option}` must be an array' + f"Option `env` in {location} `tool.hatch.envs.{env_name}.overrides.{source}." + f"{condition}.{option}` must be an array" ) raise TypeError(message) @@ -348,14 +348,14 @@ def _resolve_condition(env_name, option, source, condition, condition_value, con for i, entry in enumerate(env_vars, 1): if not isinstance(entry, str): message = ( - f'Item #{i} in option `env` in {location} `tool.hatch.envs.{env_name}.overrides.{source}.' - f'{condition}.{option}` must be a string' + f"Item #{i} in option `env` in {location} `tool.hatch.envs.{env_name}.overrides.{source}." + f"{condition}.{option}` must be a string" ) raise TypeError(message) # Allow matching empty strings - if '=' in entry: - env_var, _, value = entry.partition('=') + if "=" in entry: + env_var, _, value = entry.partition("=") required_env_vars[env_var] = value else: required_env_vars[entry] = None @@ -381,11 +381,11 @@ def __init__(self, data_dir: Path, project_path: Path): self.__project_path = project_path def dependency_hash(self, environment: EnvironmentInterface) -> str: - return self._read(environment).get('dependency_hash', '') + return self._read(environment).get("dependency_hash", "") def update_dependency_hash(self, environment: EnvironmentInterface, dependency_hash: str) -> None: metadata = self._read(environment) - metadata['dependency_hash'] = dependency_hash + metadata["dependency_hash"] = dependency_hash self._write(environment, metadata) def reset(self, environment: EnvironmentInterface) -> None: @@ -411,9 +411,9 @@ def _metadata_file(self, environment: EnvironmentInterface) -> Path: from hatch.env.internal import is_isolated_environment if is_isolated_environment(environment.name, environment.config): - return self.__data_dir / '.internal' / f'{environment.name}.json' + return self.__data_dir / ".internal" / f"{environment.name}.json" - return self._storage_dir / environment.config['type'] / f'{environment.name}.json' + return self._storage_dir / environment.config["type"] / f"{environment.name}.json" @cached_property def _storage_dir(self) -> Path: diff --git a/src/hatch/project/frontend/core.py b/src/hatch/project/frontend/core.py index 382299e2a..7112eb072 100644 --- a/src/hatch/project/frontend/core.py +++ b/src/hatch/project/frontend/core.py @@ -29,99 +29,99 @@ def hatch(self) -> HatchBuildFrontend: def build_sdist(self, directory: Path) -> Path: with self.__env.fs_context() as fs_context: - output_context = fs_context.join('output') + output_context = fs_context.join("output") output_context.local_path.ensure_dir_exists() script = self.scripts.build_sdist(project_root=self.__env.project_root, output_dir=output_context.env_path) - script_context = fs_context.join('build_sdist.py') + script_context = fs_context.join("build_sdist.py") script_context.local_path.parent.ensure_dir_exists() script_context.local_path.write_text(script) script_context.sync_env() context = ExecutionContext(self.__env) - context.add_shell_command(['python', '-u', script_context.env_path]) + context.add_shell_command(["python", "-u", script_context.env_path]) self.__env.app.execute_context(context) output_context.sync_local() - output_path = output_context.local_path / 'output.json' + output_path = output_context.local_path / "output.json" output = json.loads(output_path.read_text()) - work_dir = output_context.local_path / 'work' - artifact_path = Path(work_dir / output['return_val']) + work_dir = output_context.local_path / "work" + artifact_path = Path(work_dir / output["return_val"]) artifact_path.move(directory) return directory / artifact_path.name def build_wheel(self, directory: Path) -> Path: with self.__env.fs_context() as fs_context: - output_context = fs_context.join('output') + output_context = fs_context.join("output") output_context.local_path.ensure_dir_exists() script = self.scripts.build_wheel(project_root=self.__env.project_root, output_dir=output_context.env_path) - script_context = fs_context.join('build_wheel.py') + script_context = fs_context.join("build_wheel.py") script_context.local_path.parent.ensure_dir_exists() script_context.local_path.write_text(script) script_context.sync_env() context = ExecutionContext(self.__env) - context.add_shell_command(['python', '-u', script_context.env_path]) + context.add_shell_command(["python", "-u", script_context.env_path]) self.__env.app.execute_context(context) output_context.sync_local() - output_path = output_context.local_path / 'output.json' + output_path = output_context.local_path / "output.json" output = json.loads(output_path.read_text()) - work_dir = output_context.local_path / 'work' - artifact_path = Path(work_dir / output['return_val']) + work_dir = output_context.local_path / "work" + artifact_path = Path(work_dir / output["return_val"]) artifact_path.move(directory) return directory / artifact_path.name - def get_requires(self, build: Literal['sdist', 'wheel', 'editable']) -> list[str]: + def get_requires(self, build: Literal["sdist", "wheel", "editable"]) -> list[str]: with self.__env.fs_context() as fs_context: - output_context = fs_context.join('output') + output_context = fs_context.join("output") output_context.local_path.ensure_dir_exists() script = self.scripts.get_requires( project_root=self.__env.project_root, output_dir=output_context.env_path, build=build ) - script_context = fs_context.join(f'get_requires_{build}.py') + script_context = fs_context.join(f"get_requires_{build}.py") script_context.local_path.parent.ensure_dir_exists() script_context.local_path.write_text(script) script_context.sync_env() context = ExecutionContext(self.__env) - context.add_shell_command(['python', '-u', script_context.env_path]) + context.add_shell_command(["python", "-u", script_context.env_path]) self.__env.app.execute_context(context) output_context.sync_local() - output_path = output_context.local_path / 'output.json' + output_path = output_context.local_path / "output.json" output = json.loads(output_path.read_text()) - return output['return_val'] + return output["return_val"] def get_core_metadata(self, *, editable: bool = False) -> dict[str, Any]: from hatchling.metadata.spec import project_metadata_from_core_metadata with self.__env.fs_context() as fs_context: - output_context = fs_context.join('output') + output_context = fs_context.join("output") output_context.local_path.ensure_dir_exists() script = self.scripts.prepare_metadata( project_root=self.__env.project_root, output_dir=output_context.env_path, editable=editable ) - script_context = fs_context.join('get_core_metadata.py') + script_context = fs_context.join("get_core_metadata.py") script_context.local_path.parent.ensure_dir_exists() script_context.local_path.write_text(script) script_context.sync_env() context = ExecutionContext(self.__env) - context.add_shell_command(['python', '-u', script_context.env_path]) + context.add_shell_command(["python", "-u", script_context.env_path]) self.__env.app.execute_context(context) output_context.sync_local() - output_path = output_context.local_path / 'output.json' + output_path = output_context.local_path / "output.json" output = json.loads(output_path.read_text()) - work_dir = output_context.local_path / 'work' - metadata_file = Path(work_dir) / output['return_val'] / 'METADATA' + work_dir = output_context.local_path / "work" + metadata_file = Path(work_dir) / output["return_val"] / "METADATA" return project_metadata_from_core_metadata(metadata_file.read_text()) @@ -137,45 +137,45 @@ def scripts(self) -> HatchBuildFrontendScripts: def get_build_deps(self, targets: list[str]) -> list[str]: with self.__env.fs_context() as fs_context: - output_context = fs_context.join('output') + output_context = fs_context.join("output") output_context.local_path.ensure_dir_exists() script = self.scripts.get_build_deps( project_root=self.__env.project_root, output_dir=output_context.env_path, targets=targets ) - script_context = fs_context.join(f'get_build_deps_{"_".join(targets)}.py') + script_context = fs_context.join(f"get_build_deps_{'_'.join(targets)}.py") script_context.local_path.parent.ensure_dir_exists() script_context.local_path.write_text(script) script_context.sync_env() context = ExecutionContext(self.__env) - context.add_shell_command(['python', '-u', script_context.env_path]) + context.add_shell_command(["python", "-u", script_context.env_path]) self.__env.app.execute_context(context) output_context.sync_local() - output_path = output_context.local_path / 'output.json' + output_path = output_context.local_path / "output.json" output: list[str] = json.loads(output_path.read_text()) return output def get_core_metadata(self) -> dict[str, Any]: with self.__env.fs_context() as fs_context: - output_context = fs_context.join('output') + output_context = fs_context.join("output") output_context.local_path.ensure_dir_exists() script = self.scripts.get_core_metadata( project_root=self.__env.project_root, output_dir=output_context.env_path ) - script_context = fs_context.join('get_core_metadata.py') + script_context = fs_context.join("get_core_metadata.py") script_context.local_path.parent.ensure_dir_exists() script_context.local_path.write_text(script) script_context.sync_env() context = ExecutionContext(self.__env) - context.add_shell_command(['python', '-u', script_context.env_path]) + context.add_shell_command(["python", "-u", script_context.env_path]) self.__env.app.execute_context(context) output_context.sync_local() - output_path = output_context.local_path / 'output.json' + output_path = output_context.local_path / "output.json" output: dict[str, Any] = json.loads(output_path.read_text()) return output @@ -190,9 +190,9 @@ def get_required_build_deps(self, targets: list[str]) -> list[str]: # Remove any build hooks that are known to not define any dependencies dynamically hooks.difference_update(( # Built-in - 'version', + "version", # Popular third-party - 'vcs', + "vcs", )) if hooks: @@ -209,7 +209,7 @@ def __init__(self, project: Project, env: EnvironmentInterface) -> None: @staticmethod def inject_data(script: str, data: dict[str, Any]) -> str: # All scripts have a constant dictionary on top - return script.replace('{}', repr(data), 1) + return script.replace("{}", repr(data), 1) class StandardBuildFrontendScripts(BuildFrontendScripts): @@ -224,13 +224,13 @@ def get_runner_script( return self.inject_data( runner_script(), { - 'project_root': project_root, - 'output_dir': output_dir, - 'hook': hook, - 'kwargs': kwargs, - 'backend': self._project.metadata.build.build_backend, - 'backend_path': self._env.pathsep.join(self._project.metadata.build.backend_path), - 'hook_caller_script': hook_caller_script(), + "project_root": project_root, + "output_dir": output_dir, + "hook": hook, + "kwargs": kwargs, + "backend": self._project.metadata.build.build_backend, + "backend_path": self._env.pathsep.join(self._project.metadata.build.backend_path), + "hook_caller_script": hook_caller_script(), }, ) @@ -239,37 +239,37 @@ def get_requires( *, project_root: str, output_dir: str, - build: Literal['sdist', 'wheel', 'editable'], + build: Literal["sdist", "wheel", "editable"], ) -> str: return self.get_runner_script( project_root=project_root, output_dir=output_dir, - hook=f'get_requires_for_build_{build}', - kwargs={'config_settings': None}, + hook=f"get_requires_for_build_{build}", + kwargs={"config_settings": None}, ) def prepare_metadata(self, *, output_dir: str, project_root: str, editable: bool = False) -> str: return self.get_runner_script( project_root=project_root, output_dir=output_dir, - hook='prepare_metadata_for_build_editable' if editable else 'prepare_metadata_for_build_wheel', - kwargs={'work_dir': 'metadata_directory', 'config_settings': None, '_allow_fallback': True}, + hook="prepare_metadata_for_build_editable" if editable else "prepare_metadata_for_build_wheel", + kwargs={"work_dir": "metadata_directory", "config_settings": None, "_allow_fallback": True}, ) def build_wheel(self, *, output_dir: str, project_root: str, editable: bool = False) -> str: return self.get_runner_script( project_root=project_root, output_dir=output_dir, - hook='build_editable' if editable else 'build_wheel', - kwargs={'work_dir': 'wheel_directory', 'config_settings': None, 'metadata_directory': None}, + hook="build_editable" if editable else "build_wheel", + kwargs={"work_dir": "wheel_directory", "config_settings": None, "metadata_directory": None}, ) def build_sdist(self, *, output_dir: str, project_root: str) -> str: return self.get_runner_script( project_root=project_root, output_dir=output_dir, - hook='build_sdist', - kwargs={'work_dir': 'sdist_directory', 'config_settings': None}, + hook="build_sdist", + kwargs={"work_dir": "sdist_directory", "config_settings": None}, ) @@ -278,9 +278,9 @@ def get_build_deps(self, *, output_dir: str, project_root: str, targets: list[st return self.inject_data( hatch_build_deps_script(), { - 'project_root': project_root, - 'output_dir': output_dir, - 'targets': targets, + "project_root": project_root, + "output_dir": output_dir, + "targets": targets, }, ) @@ -288,8 +288,8 @@ def get_core_metadata(self, *, output_dir: str, project_root: str) -> str: return self.inject_data( hatch_core_metadata_script(), { - 'project_root': project_root, - 'output_dir': output_dir, + "project_root": project_root, + "output_dir": output_dir, }, ) @@ -298,29 +298,29 @@ def get_core_metadata(self, *, output_dir: str, project_root: str) -> str: def hook_caller_script() -> str: from importlib.resources import files - script = files('pyproject_hooks._in_process') / '_in_process.py' - return script.read_text(encoding='utf-8') + script = files("pyproject_hooks._in_process") / "_in_process.py" + return script.read_text(encoding="utf-8") @cache def runner_script() -> str: from importlib.resources import files - script = files('hatch.project.frontend.scripts') / 'standard.py' - return script.read_text(encoding='utf-8') + script = files("hatch.project.frontend.scripts") / "standard.py" + return script.read_text(encoding="utf-8") @cache def hatch_build_deps_script() -> str: from importlib.resources import files - script = files('hatch.project.frontend.scripts') / 'build_deps.py' - return script.read_text(encoding='utf-8') + script = files("hatch.project.frontend.scripts") / "build_deps.py" + return script.read_text(encoding="utf-8") @cache def hatch_core_metadata_script() -> str: from importlib.resources import files - script = files('hatch.project.frontend.scripts') / 'core_metadata.py' - return script.read_text(encoding='utf-8') + script = files("hatch.project.frontend.scripts") / "core_metadata.py" + return script.read_text(encoding="utf-8") diff --git a/src/hatch/project/frontend/scripts/build_deps.py b/src/hatch/project/frontend/scripts/build_deps.py index 296abdeb2..16e8033ef 100644 --- a/src/hatch/project/frontend/scripts/build_deps.py +++ b/src/hatch/project/frontend/scripts/build_deps.py @@ -11,9 +11,9 @@ def main() -> None: - project_root: str = RUNNER['project_root'] - output_dir: str = RUNNER['output_dir'] - targets: list[str] = RUNNER['targets'] + project_root: str = RUNNER["project_root"] + output_dir: str = RUNNER["output_dir"] + targets: list[str] = RUNNER["targets"] app = Application() plugin_manager = PluginManager() @@ -32,9 +32,9 @@ def main() -> None: dependencies[dependency] = None output = json.dumps(list(dependencies)) - with open(os.path.join(output_dir, 'output.json'), 'w', encoding='utf-8') as f: + with open(os.path.join(output_dir, "output.json"), "w", encoding="utf-8") as f: f.write(output) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/src/hatch/project/frontend/scripts/core_metadata.py b/src/hatch/project/frontend/scripts/core_metadata.py index 930d3731b..47f23a34f 100644 --- a/src/hatch/project/frontend/scripts/core_metadata.py +++ b/src/hatch/project/frontend/scripts/core_metadata.py @@ -11,8 +11,8 @@ def main() -> None: - project_root: str = RUNNER['project_root'] - output_dir: str = RUNNER['output_dir'] + project_root: str = RUNNER["project_root"] + output_dir: str = RUNNER["output_dir"] project_metadata = ProjectMetadata(project_root, PluginManager()) core_metadata = resolve_metadata_fields(project_metadata) @@ -21,9 +21,9 @@ def main() -> None: core_metadata.pop(key) output = json.dumps(core_metadata) - with open(os.path.join(output_dir, 'output.json'), 'w', encoding='utf-8') as f: + with open(os.path.join(output_dir, "output.json"), "w", encoding="utf-8") as f: f.write(output) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/src/hatch/project/frontend/scripts/standard.py b/src/hatch/project/frontend/scripts/standard.py index 3753b20f9..003bf5267 100644 --- a/src/hatch/project/frontend/scripts/standard.py +++ b/src/hatch/project/frontend/scripts/standard.py @@ -11,38 +11,38 @@ def main() -> int: - project_root: str = RUNNER['project_root'] - output_dir: str = RUNNER['output_dir'] - hook: str = RUNNER['hook'] - kwargs: dict[str, str] = RUNNER['kwargs'] - backend: str = RUNNER['backend'] - backend_path: str = RUNNER['backend_path'] - hook_caller_script: str = RUNNER['hook_caller_script'] + project_root: str = RUNNER["project_root"] + output_dir: str = RUNNER["output_dir"] + hook: str = RUNNER["hook"] + kwargs: dict[str, str] = RUNNER["kwargs"] + backend: str = RUNNER["backend"] + backend_path: str = RUNNER["backend_path"] + hook_caller_script: str = RUNNER["hook_caller_script"] with TemporaryDirectory() as d: temp_dir = os.path.realpath(d) - control_dir = os.path.join(temp_dir, 'control') + control_dir = os.path.join(temp_dir, "control") os.mkdir(control_dir) - input_file = os.path.join(control_dir, 'input.json') - output_file = os.path.join(control_dir, 'output.json') + input_file = os.path.join(control_dir, "input.json") + output_file = os.path.join(control_dir, "output.json") env_vars = dict(os.environ) - env_vars['_PYPROJECT_HOOKS_BUILD_BACKEND'] = backend + env_vars["_PYPROJECT_HOOKS_BUILD_BACKEND"] = backend if backend_path: - env_vars['_PYPROJECT_HOOKS_BACKEND_PATH'] = backend_path + env_vars["_PYPROJECT_HOOKS_BACKEND_PATH"] = backend_path - if 'work_dir' in kwargs: - work_dir = os.path.join(temp_dir, 'work') + if "work_dir" in kwargs: + work_dir = os.path.join(temp_dir, "work") os.mkdir(work_dir) - kwargs[kwargs.pop('work_dir')] = work_dir + kwargs[kwargs.pop("work_dir")] = work_dir else: - work_dir = '' + work_dir = "" - with open(input_file, 'w', encoding='utf-8') as f: - f.write(json.dumps({'kwargs': kwargs})) + with open(input_file, "w", encoding="utf-8") as f: + f.write(json.dumps({"kwargs": kwargs})) - script_path = os.path.join(temp_dir, 'script.py') - with open(script_path, 'w', encoding='utf-8') as f: + script_path = os.path.join(temp_dir, "script.py") + with open(script_path, "w", encoding="utf-8") as f: f.write(hook_caller_script) process = subprocess.run( @@ -54,19 +54,19 @@ def main() -> int: if process.returncode: return process.returncode - with open(output_file, encoding='utf-8') as f: + with open(output_file, encoding="utf-8") as f: output = json.loads(f.read()) - if output.get('no_backend', False): - sys.stderr.write(f'{output["traceback"]}\n{output["backend_error"]}\n') + if output.get("no_backend", False): + sys.stderr.write(f"{output['traceback']}\n{output['backend_error']}\n") return 1 - if output.get('unsupported', False): - sys.stderr.write(output['traceback']) + if output.get("unsupported", False): + sys.stderr.write(output["traceback"]) return 1 - if output.get('hook_missing', False): - sys.stderr.write(f'Build backend API `{backend}` is missing hook: {output["missing_hook_name"]}\n') + if output.get("hook_missing", False): + sys.stderr.write(f"Build backend API `{backend}` is missing hook: {output['missing_hook_name']}\n") return 1 shutil.move(output_file, output_dir) @@ -76,7 +76,7 @@ def main() -> int: return 0 -if __name__ == '__main__': +if __name__ == "__main__": code = main() sys.stderr.flush() os._exit(code) diff --git a/src/hatch/project/utils.py b/src/hatch/project/utils.py index 02076cf53..c408de387 100644 --- a/src/hatch/project/utils.py +++ b/src/hatch/project/utils.py @@ -7,10 +7,10 @@ def parse_script_command(command: str) -> tuple[str, str, bool]: - possible_script, _, args = command.partition(' ') - if possible_script == '-': + possible_script, _, args = command.partition(" ") + if possible_script == "-": ignore_exit_code = True - possible_script, _, args = args.partition(' ') + possible_script, _, args = args.partition(" ") else: ignore_exit_code = False @@ -20,9 +20,9 @@ def parse_script_command(command: str) -> tuple[str, str, bool]: def format_script_commands(*, commands: list[str], args: str, ignore_exit_code: bool) -> Iterable[str]: for command in commands: if args: - yield f'{command} {args}' - elif ignore_exit_code and not command.startswith('- '): - yield f'- {command}' + yield f"{command} {args}" + elif ignore_exit_code and not command.startswith("- "): + yield f"- {command}" else: yield command @@ -35,17 +35,17 @@ def parse_inline_script_metadata(script: str) -> dict[str, Any] | None: from hatch.utils.toml import load_toml_data - block_type = 'script' - pattern = re.compile(r'(?m)^# /// (?P[a-zA-Z0-9-]+)$\s(?P(^#(| .*)$\s)+)^# ///$') - matches = list(filter(lambda m: m.group('type') == block_type, pattern.finditer(script))) + block_type = "script" + pattern = re.compile(r"(?m)^# /// (?P[a-zA-Z0-9-]+)$\s(?P(^#(| .*)$\s)+)^# ///$") + matches = list(filter(lambda m: m.group("type") == block_type, pattern.finditer(script))) if len(matches) > 1: - message = f'Multiple inline metadata blocks found for type: {block_type}' + message = f"Multiple inline metadata blocks found for type: {block_type}" raise ValueError(message) if len(matches) == 1: - content = ''.join( - line[2:] if line.startswith('# ') else line[1:] - for line in matches[0].group('content').splitlines(keepends=True) + content = "".join( + line[2:] if line.startswith("# ") else line[1:] + for line in matches[0].group("content").splitlines(keepends=True) ) return load_toml_data(content) diff --git a/src/hatch/publish/auth.py b/src/hatch/publish/auth.py index 79790a543..7f83cdd7e 100644 --- a/src/hatch/publish/auth.py +++ b/src/hatch/publish/auth.py @@ -13,7 +13,7 @@ def __init__( repo_config: dict[str, str], ): self._app = app - self._pwu_path = cache_dir / 'previous_working_users.json' + self._pwu_path = cache_dir / "previous_working_users.json" self._options = options self._repo = repo self._repo_config = repo_config @@ -40,7 +40,7 @@ def __get_password(self) -> str: # this method doesn't consider .pypirc as the __password attribute would have # been set when it was looked up during username retrieval - password = self._options.get('auth') or self._repo_config.get('auth') + password = self._options.get("auth") or self._repo_config.get("auth") if password is not None: return password @@ -50,27 +50,27 @@ def __get_password(self) -> str: if password is not None: return password - if self._options['no_prompt']: - self._app.abort('Missing required option: auth') + if self._options["no_prompt"]: + self._app.abort("Missing required option: auth") self.__password_was_read = True - return self._app.prompt('Password / Token', hide_input=True) + return self._app.prompt("Password / Token", hide_input=True) def __get_username(self) -> str: username = ( - self._options.get('user') - or self._repo_config.get('user') + self._options.get("user") + or self._repo_config.get("user") or self._read_pypirc() or self._read_previous_working_user_data() ) if username is not None: return username - if self._options['no_prompt']: - self._app.abort('Missing required option: user') + if self._options["no_prompt"]: + self._app.abort("Missing required option: user") self.__username_was_read = True - return self._app.prompt(f"Username for '{self._repo_config['url']}' [__token__]") or '__token__' + return self._app.prompt(f"Username for '{self._repo_config['url']}' [__token__]") or "__token__" def _read_previous_working_user_data(self) -> str | None: if self._pwu_path.is_file(): @@ -85,18 +85,18 @@ def _read_pypirc(self) -> str | None: import configparser pypirc = configparser.ConfigParser() - pypirc.read(Path.home() / '.pypirc') - repo = self._repo or 'pypi' + pypirc.read(Path.home() / ".pypirc") + repo = self._repo or "pypi" if pypirc.has_section(repo): - self.__password = pypirc.get(section=repo, option='password', fallback=None) - return pypirc.get(section=repo, option='username', fallback=None) + self.__password = pypirc.get(section=repo, option="password", fallback=None) + return pypirc.get(section=repo, option="username", fallback=None) - repo_url = self._repo_config['url'] + repo_url = self._repo_config["url"] for section in pypirc.sections(): - if pypirc.get(section=section, option='repository', fallback=None) == repo_url: - self.__password = pypirc.get(section=section, option='password', fallback=None) - return pypirc.get(section=section, option='username', fallback=None) + if pypirc.get(section=section, option="repository", fallback=None) == repo_url: + self.__password = pypirc.get(section=section, option="password", fallback=None) + return pypirc.get(section=section, option="username", fallback=None) return None diff --git a/src/hatch/publish/index.py b/src/hatch/publish/index.py index bc588972f..d0f5f4714 100644 --- a/src/hatch/publish/index.py +++ b/src/hatch/publish/index.py @@ -12,31 +12,31 @@ class IndexPublisher(PublisherInterface): - PLUGIN_NAME = 'index' + PLUGIN_NAME = "index" def get_repos(self): global_plugin_config = self.plugin_config.copy() - defined_repos = self.plugin_config.pop('repos', {}) - self.plugin_config.pop('repo', None) + defined_repos = self.plugin_config.pop("repos", {}) + self.plugin_config.pop("repo", None) # Normalize type repos = {} for repo, data in defined_repos.items(): if isinstance(data, str): - repos[repo] = {'url': data} + repos[repo] = {"url": data} elif not isinstance(data, dict): - self.app.abort(f'Hatch config field `publish.index.repos.{repo}` must be a string or a mapping') - elif 'url' not in data: - self.app.abort(f'Hatch config field `publish.index.repos.{repo}` must define a `url` key') + self.app.abort(f"Hatch config field `publish.index.repos.{repo}` must be a string or a mapping") + elif "url" not in data: + self.app.abort(f"Hatch config field `publish.index.repos.{repo}` must define a `url` key") else: repos[repo] = data # Ensure PyPI correct for repo, url in ( - ('main', 'https://upload.pypi.org/legacy/'), - ('test', 'https://test.pypi.org/legacy/'), + ("main", "https://upload.pypi.org/legacy/"), + ("test", "https://test.pypi.org/legacy/"), ): - repos.setdefault(repo, {})['url'] = url + repos.setdefault(repo, {})["url"] = url # Populate defaults for config in repos.values(): @@ -60,9 +60,9 @@ def publish(self, artifacts: list, options: dict): artifacts = [DEFAULT_BUILD_DIRECTORY] - repo = options['repo'] if 'repo' in options else self.plugin_config.get('repo', 'main') + repo = options["repo"] if "repo" in options else self.plugin_config.get("repo", "main") repos = self.get_repos() - repo_config: dict[str, str] = repos[repo] if repo in repos else {'url': repo} + repo_config: dict[str, str] = repos[repo] if repo in repos else {"url": repo} credentials = AuthenticationCredentials( app=self.app, cache_dir=self.cache_dir, @@ -72,12 +72,12 @@ def publish(self, artifacts: list, options: dict): ) index = PackageIndex( - repo_config['url'], + repo_config["url"], user=credentials.username, auth=credentials.password, - ca_cert=options.get('ca_cert', repo_config.get('ca-cert')), - client_cert=options.get('client_cert', repo_config.get('client-cert')), - client_key=options.get('client_key', repo_config.get('client-key')), + ca_cert=options.get("ca_cert", repo_config.get("ca-cert")), + client_cert=options.get("client_cert", repo_config.get("client-cert")), + client_key=options.get("client_key", repo_config.get("client-key")), ) existing_artifacts: dict[str, set[str]] = {} @@ -87,27 +87,27 @@ def publish(self, artifacts: list, options: dict): artifacts_found = False for artifact in recurse_artifacts(artifacts, self.root): - if artifact.name.endswith('.whl'): + if artifact.name.endswith(".whl"): data = get_wheel_form_data(artifact) - elif artifact.name.endswith('.tar.gz'): + elif artifact.name.endswith(".tar.gz"): data = get_sdist_form_data(artifact) else: continue artifacts_found = True - for field in ('name', 'version'): + for field in ("name", "version"): if field not in data: - self.app.abort(f'Missing required field `{field}` in artifact: {artifact}') + self.app.abort(f"Missing required field `{field}` in artifact: {artifact}") try: displayed_path = str(artifact.relative_to(self.root)) except ValueError: displayed_path = str(artifact) - self.app.display_info(f'{displayed_path} ...', end=' ') + self.app.display_info(f"{displayed_path} ...", end=" ") - project_name = normalize_project_name(data['name']) + project_name = normalize_project_name(data["name"]) if project_name not in existing_artifacts: try: response = index.get_simple_api(project_name) @@ -118,23 +118,23 @@ def publish(self, artifacts: list, options: dict): existing_artifacts[project_name] = set(parse_artifacts(response.text)) if artifact.name in existing_artifacts[project_name]: - self.app.display_warning('already exists') + self.app.display_warning("already exists") continue try: index.upload_artifact(artifact, data) except Exception as e: # noqa: BLE001 - self.app.display_error('failed') - self.app.abort(f'Error uploading to repository: {index.repo} - {e}'.replace(index.auth, '*****')) + self.app.display_error("failed") + self.app.abort(f"Error uploading to repository: {index.repo} - {e}".replace(index.auth, "*****")) else: - self.app.display_success('success') + self.app.display_success("success") existing_artifacts[project_name].add(artifact.name) - project_versions[project_name][data['version']] = None + project_versions[project_name][data["version"]] = None - if not options['initialize_auth']: + if not options["initialize_auth"]: if not artifacts_found: - self.app.abort('No artifacts found') + self.app.abort("No artifacts found") elif not project_versions: self.app.abort(code=0) @@ -142,7 +142,7 @@ def publish(self, artifacts: list, options: dict): self.app.display_info() self.app.display_mini_header(project_name) for version in versions: - self.app.display_info(str(index.urls.project.child(project_name, version, '').to_iri())) + self.app.display_info(str(index.urls.project.child(project_name, version, "").to_iri())) credentials.write_updated_data() @@ -160,5 +160,5 @@ def recurse_artifacts(artifacts: list, root) -> Iterable[Path]: def parse_artifacts(artifact_payload): - for match in re.finditer(r']+>([^<]+)', artifact_payload): + for match in re.finditer(r"]+>([^<]+)", artifact_payload): yield match.group(1) diff --git a/src/hatch/publish/plugin/interface.py b/src/hatch/publish/plugin/interface.py index 30bb12159..e03e35f1a 100644 --- a/src/hatch/publish/plugin/interface.py +++ b/src/hatch/publish/plugin/interface.py @@ -28,7 +28,7 @@ def hatch_register_publisher(): ``` """ - PLUGIN_NAME = '' + PLUGIN_NAME = "" """The name used for selection.""" def __init__(self, app, root, cache_dir, project_config, plugin_config): @@ -90,15 +90,15 @@ def disable(self): [plugin configuration](reference.md#hatch.publish.plugin.interface.PublisherInterface.plugin_config). """ if self.__disable is None: - if 'disable' in self.project_config: - disable = self.project_config['disable'] + if "disable" in self.project_config: + disable = self.project_config["disable"] if not isinstance(disable, bool): - message = f'Field `tool.hatch.publish.{self.PLUGIN_NAME}.disable` must be a boolean' + message = f"Field `tool.hatch.publish.{self.PLUGIN_NAME}.disable` must be a boolean" raise TypeError(message) else: - disable = self.plugin_config.get('disable', False) + disable = self.plugin_config.get("disable", False) if not isinstance(disable, bool): - message = f'Global plugin configuration `publish.{self.PLUGIN_NAME}.disable` must be a boolean' + message = f"Global plugin configuration `publish.{self.PLUGIN_NAME}.disable` must be a boolean" raise TypeError(message) self.__disable = disable diff --git a/src/hatch/python/core.py b/src/hatch/python/core.py index b0914b7db..b05d923f9 100644 --- a/src/hatch/python/core.py +++ b/src/hatch/python/core.py @@ -43,7 +43,7 @@ def needs_update(self) -> bool: @classmethod def metadata_filename(cls) -> str: - return 'hatch-dist.json' + return "hatch-dist.json" class PythonManager: @@ -70,7 +70,7 @@ def get_installed(self) -> dict[str, InstalledDistribution]: continue metadata = json.loads(metadata_file.read_text()) - distribution = get_distribution(path.name, source=metadata.get('source', '')) + distribution = get_distribution(path.name, source=metadata.get("source", "")) if not (path / distribution.python_path).is_file(): continue @@ -94,7 +94,7 @@ def install(self, identifier: str) -> InstalledDistribution: download_file(archive_path, dist.source, follow_redirects=True) dist.unpack(archive_path, unpack_path) - backup_path = path.with_suffix('.bak') + backup_path = path.with_suffix(".bak") if backup_path.is_dir(): backup_path.wait_for_dir_removed() @@ -115,7 +115,7 @@ def install(self, identifier: str) -> InstalledDistribution: raise - metadata = {'source': dist.source, 'python_path': dist.python_path} + metadata = {"source": dist.source, "python_path": dist.python_path} metadata_file = path / InstalledDistribution.metadata_filename() metadata_file.write_text(json.dumps(metadata, indent=2)) diff --git a/src/hatch/python/resolve.py b/src/hatch/python/resolve.py index d893e6152..cf17a29b7 100644 --- a/src/hatch/python/resolve.py +++ b/src/hatch/python/resolve.py @@ -22,7 +22,7 @@ def custom_env_var(prefix: str, name: str) -> str: - return f'{prefix}{name.upper().replace(".", "_")}' + return f"{prefix}{name.upper().replace('.', '_')}" def get_custom_source(name: str) -> str | None: @@ -52,44 +52,44 @@ def source(self) -> str: @cached_property def archive_name(self) -> str: - return self.source.rsplit('/', 1)[-1] + return self.source.rsplit("/", 1)[-1] def unpack(self, archive: Path, directory: Path) -> None: - if self.source.endswith('.zip'): + if self.source.endswith(".zip"): import zipfile - with zipfile.ZipFile(archive, 'r') as zf: + with zipfile.ZipFile(archive, "r") as zf: zf.extractall(directory) - elif self.source.endswith(('.tar.gz', '.tgz')): + elif self.source.endswith((".tar.gz", ".tgz")): import tarfile - with tarfile.open(archive, 'r:gz') as tf: + with tarfile.open(archive, "r:gz") as tf: if sys.version_info[:2] >= (3, 12): - tf.extractall(directory, filter='data') + tf.extractall(directory, filter="data") else: tf.extractall(directory) # noqa: S202 - elif self.source.endswith(('.tar.bz2', '.bz2')): + elif self.source.endswith((".tar.bz2", ".bz2")): import tarfile - with tarfile.open(archive, 'r:bz2') as tf: + with tarfile.open(archive, "r:bz2") as tf: if sys.version_info[:2] >= (3, 12): - tf.extractall(directory, filter='data') + tf.extractall(directory, filter="data") else: tf.extractall(directory) # noqa: S202 - elif self.source.endswith(('.tar.zst', '.tar.zstd')): + elif self.source.endswith((".tar.zst", ".tar.zstd")): import tarfile import zstandard - with open(archive, 'rb') as ifh: + with open(archive, "rb") as ifh: dctx = zstandard.ZstdDecompressor() - with dctx.stream_reader(ifh) as reader, tarfile.open(mode='r|', fileobj=reader) as tf: + with dctx.stream_reader(ifh) as reader, tarfile.open(mode="r|", fileobj=reader) as tf: if sys.version_info[:2] >= (3, 12): - tf.extractall(directory, filter='data') + tf.extractall(directory, filter="data") else: tf.extractall(directory) # noqa: S202 else: - message = f'Unknown archive type: {archive}' + message = f"Unknown archive type: {archive}" raise ValueError(message) @property @@ -109,31 +109,31 @@ def version(self) -> Version: from packaging.version import Version if (custom_version := get_custom_version(self.name)) is not None: - return Version(f'{CUSTOM_DISTRIBUTION_VERSION_EPOCH}!{custom_version}') + return Version(f"{CUSTOM_DISTRIBUTION_VERSION_EPOCH}!{custom_version}") # .../cpython-3.12.0%2B20231002-... # .../cpython-3.7.9-... - _, _, remaining = self.source.partition('/cpython-') + _, _, remaining = self.source.partition("/cpython-") # 3.12.0%2B20231002-... # 3.7.9-... - version = remaining.split('%2B')[0] if '%2B' in remaining else remaining.split('-')[0] - return Version(f'0!{version}') + version = remaining.split("%2B")[0] if "%2B" in remaining else remaining.split("-")[0] + return Version(f"0!{version}") @cached_property def python_path(self) -> str: if (custom_path := get_custom_path(self.name)) is not None: return custom_path - if self.name == '3.7': - if sys.platform == 'win32': - return r'python\install\python.exe' + if self.name == "3.7": + if sys.platform == "win32": + return r"python\install\python.exe" - return 'python/install/bin/python3' + return "python/install/bin/python3" - if sys.platform == 'win32': - return r'python\python.exe' + if sys.platform == "win32": + return r"python\python.exe" - return 'python/bin/python3' + return "python/bin/python3" class PyPyOfficialDistribution(Distribution): @@ -142,11 +142,11 @@ def version(self) -> Version: from packaging.version import Version if (custom_version := get_custom_version(self.name)) is not None: - return Version(f'{CUSTOM_DISTRIBUTION_VERSION_EPOCH}!{custom_version}') + return Version(f"{CUSTOM_DISTRIBUTION_VERSION_EPOCH}!{custom_version}") - *_, remaining = self.source.partition('/pypy/') - _, version, *_ = remaining.split('-') - return Version(f'0!{version[1:]}') + *_, remaining = self.source.partition("/pypy/") + _, version, *_ = remaining.split("-") + return Version(f"0!{version[1:]}") @cached_property def python_path(self) -> str: @@ -154,35 +154,35 @@ def python_path(self) -> str: return custom_path directory = self.archive_name - for extension in ('.tar.bz2', '.zip'): + for extension in (".tar.bz2", ".zip"): if directory.endswith(extension): directory = directory[: -len(extension)] break - if sys.platform == 'win32': - return rf'{directory}\pypy.exe' + if sys.platform == "win32": + return rf"{directory}\pypy.exe" - return f'{directory}/bin/pypy' + return f"{directory}/bin/pypy" -def get_distribution(name: str, source: str = '', variant_cpu: str = '', variant_gil: str = '') -> Distribution: +def get_distribution(name: str, source: str = "", variant_cpu: str = "", variant_gil: str = "") -> Distribution: if source: return _get_distribution_class(source)(name, source) if name not in DISTRIBUTIONS: - message = f'Unknown distribution: {name}' + message = f"Unknown distribution: {name}" raise PythonDistributionUnknownError(message) arch = platform.machine().lower() - if sys.platform == 'win32': - system = 'windows' - abi = 'msvc' - elif sys.platform == 'darwin': - system = 'macos' - abi = '' + if sys.platform == "win32": + system = "windows" + abi = "msvc" + elif sys.platform == "darwin": + system = "macos" + abi = "" else: - system = 'linux' - abi = 'gnu' if any(platform.libc_ver()) else 'musl' + system = "linux" + abi = "gnu" if any(platform.libc_ver()) else "musl" if not variant_cpu: variant_cpu = _get_default_variant_cpu(name, system, arch) @@ -194,7 +194,7 @@ def get_distribution(name: str, source: str = '', variant_cpu: str = '', variant keys: dict[tuple, str] = DISTRIBUTIONS[name] if key not in keys: - message = f'Could not find a default source for {name=} {system=} {arch=} {abi=} {variant_cpu=} {variant_gil=}' + message = f"Could not find a default source for {name=} {system=} {arch=} {abi=} {variant_cpu=} {variant_gil=}" raise PythonDistributionResolutionError(message) source = keys[key] @@ -216,10 +216,10 @@ def get_compatible_distributions() -> dict[str, Distribution]: def _guess_linux_variant_cpu() -> str: # Use the highest that we know is most common when we can't parse CPU data - default = 'v3' + default = "v3" try: # Don't use our utility Path so we can properly mock - with open('/proc/cpuinfo', encoding='utf-8') as f: + with open("/proc/cpuinfo", encoding="utf-8") as f: contents = f.read() except OSError: return default @@ -228,25 +228,25 @@ def _guess_linux_variant_cpu() -> str: # instructions for each architecture variant and # https://github.com/torvalds/linux/blob/master/arch/x86/include/asm/cpufeatures.h # for the corresponding Linux flags - v2_flags = {'cx16', 'lahf_lm', 'popcnt', 'pni', 'sse4_1', 'sse4_2', 'ssse3'} - v3_flags = {'avx', 'avx2', 'bmi1', 'bmi2', 'f16c', 'fma', 'movbe', 'xsave'} | v2_flags - v4_flags = {'avx512f', 'avx512bw', 'avx512cd', 'avx512dq', 'avx512vl'} | v3_flags + v2_flags = {"cx16", "lahf_lm", "popcnt", "pni", "sse4_1", "sse4_2", "ssse3"} + v3_flags = {"avx", "avx2", "bmi1", "bmi2", "f16c", "fma", "movbe", "xsave"} | v2_flags + v4_flags = {"avx512f", "avx512bw", "avx512cd", "avx512dq", "avx512vl"} | v3_flags for line in contents.splitlines(): - key, _, value = line.partition(':') - if key.strip() == 'flags': + key, _, value = line.partition(":") + if key.strip() == "flags": flags = set(value.strip().split()) if flags.issuperset(v4_flags): - return 'v4' + return "v4" if flags.issuperset(v3_flags): - return 'v3' + return "v3" if flags.issuperset(v2_flags): - return 'v2' + return "v2" - return 'v1' + return "v1" return default @@ -255,36 +255,36 @@ def _get_default_variant_cpu(name: str, system: str, arch: str) -> str: # not PyPy if name[0].isdigit(): variant = os.environ.get( - 'HATCH_PYTHON_VARIANT_CPU', + "HATCH_PYTHON_VARIANT_CPU", # Legacy name - os.environ.get(f'HATCH_PYTHON_VARIANT_{system.upper()}', ''), + os.environ.get(f"HATCH_PYTHON_VARIANT_{system.upper()}", ""), ).lower() # https://gregoryszorc.com/docs/python-build-standalone/main/running.html - if system == 'linux' and arch == 'x86_64': + if system == "linux" and arch == "x86_64": # Intel-specific optimizations depending on age of release if variant: return variant - if name == '3.8': - return 'v1' + if name == "3.8": + return "v1" - if name != '3.7': + if name != "3.7": return _guess_linux_variant_cpu() - return '' + return "" def _get_default_variant_gil() -> str: - return os.environ.get('HATCH_PYTHON_VARIANT_GIL', '').lower() + return os.environ.get("HATCH_PYTHON_VARIANT_GIL", "").lower() def _get_distribution_class(source: str) -> type[Distribution]: - if source.startswith('https://github.com/indygreg/python-build-standalone/releases/download/'): + if source.startswith("https://github.com/indygreg/python-build-standalone/releases/download/"): return CPythonStandaloneDistribution - if source.startswith('https://downloads.python.org/pypy/'): + if source.startswith("https://downloads.python.org/pypy/"): return PyPyOfficialDistribution - message = f'Unknown distribution source: {source}' + message = f"Unknown distribution source: {source}" raise ValueError(message) diff --git a/src/hatch/template/__init__.py b/src/hatch/template/__init__.py index d1008812a..b641d6e90 100644 --- a/src/hatch/template/__init__.py +++ b/src/hatch/template/__init__.py @@ -8,7 +8,7 @@ class File: - def __init__(self, path: Path | None, contents: str = ''): + def __init__(self, path: Path | None, contents: str = ""): self.path = path self.contents = contents self.feature = None @@ -19,7 +19,7 @@ def write(self, root): path = root / self.path path.ensure_parent_dir_exists() - path.write_text(self.contents, encoding='utf-8') + path.write_text(self.contents, encoding="utf-8") def find_template_files(module): diff --git a/src/hatch/template/default.py b/src/hatch/template/default.py index 450784352..21bfbaa6c 100644 --- a/src/hatch/template/default.py +++ b/src/hatch/template/default.py @@ -5,42 +5,42 @@ class DefaultTemplate(TemplateInterface): - PLUGIN_NAME = 'default' + PLUGIN_NAME = "default" def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) - self.plugin_config.setdefault('ci', False) - self.plugin_config.setdefault('src-layout', True) - self.plugin_config.setdefault('tests', True) + self.plugin_config.setdefault("ci", False) + self.plugin_config.setdefault("src-layout", True) + self.plugin_config.setdefault("tests", True) def initialize_config(self, config): # Default values - config['readme_file_path'] = 'README.md' - config['package_metadata_file_path'] = f'src/{config["package_name"]}/__about__.py' + config["readme_file_path"] = "README.md" + config["package_metadata_file_path"] = f"src/{config['package_name']}/__about__.py" license_data = {} # Licenses - license_ids = config['licenses']['default'] + license_ids = config["licenses"]["default"] if not license_ids: - config['license_data'] = license_data - config['license_expression'] = '' - config['license_files'] = '' - config['license_header'] = '' + config["license_data"] = license_data + config["license_expression"] = "" + config["license_files"] = "" + config["license_header"] = "" return - cached_licenses_dir = self.cache_dir / 'licenses' + cached_licenses_dir = self.cache_dir / "licenses" cached_licenses_dir.ensure_dir_exists() license_ids = sorted(set(license_ids)) for license_id in sorted(set(license_ids)): - license_file_name = f'{license_id}.txt' + license_file_name = f"{license_id}.txt" cached_license_path = cached_licenses_dir / license_file_name if not cached_license_path.is_file(): from packaging.licenses._spdx import VERSION # noqa: PLC2701 - url = f'https://raw.githubusercontent.com/spdx/license-list-data/v{VERSION}/text/{license_file_name}' + url = f"https://raw.githubusercontent.com/spdx/license-list-data/v{VERSION}/text/{license_file_name}" for _ in range(5): try: download_file(cached_license_path, url) @@ -49,57 +49,57 @@ def initialize_config(self, config): else: break - license_data[license_id] = cached_license_path.read_text(encoding='utf-8') + license_data[license_id] = cached_license_path.read_text(encoding="utf-8") - config['license_data'] = license_data - config['license_expression'] = ' OR '.join(license_data) - config['license_header'] = ( - '' - if not config['licenses']['headers'] + config["license_data"] = license_data + config["license_expression"] = " OR ".join(license_data) + config["license_header"] = ( + "" + if not config["licenses"]["headers"] else f"""\ -# SPDX-FileCopyrightText: {self.creation_time.year}-present {config['name']} <{config['email']}> +# SPDX-FileCopyrightText: {self.creation_time.year}-present {config["name"]} <{config["email"]}> # -# SPDX-License-Identifier: {config['license_expression']} +# SPDX-License-Identifier: {config["license_expression"]} """ ) if len(license_ids) == 1: - config['license_files'] = '' + config["license_files"] = "" else: - config['license_files'] = '\nlicense-files = { globs = ["LICENSES/*"] }' + config["license_files"] = '\nlicense-files = { globs = ["LICENSES/*"] }' - if config['args']['cli']: - config['dependencies'].add('click') + if config["args"]["cli"]: + config["dependencies"].add("click") - if not self.plugin_config['src-layout']: - config['package_metadata_file_path'] = f'{config["package_metadata_file_path"][4:]}' + if not self.plugin_config["src-layout"]: + config["package_metadata_file_path"] = f"{config['package_metadata_file_path'][4:]}" def get_files(self, config): files = list(find_template_files(files_default)) # Add any licenses - license_data = config['license_data'] + license_data = config["license_data"] if license_data: if len(license_data) == 1: license_id, text = next(iter(license_data.items())) license_text = get_license_text(config, license_id, text, self.creation_time) - files.append(File(Path('LICENSE.txt'), license_text)) + files.append(File(Path("LICENSE.txt"), license_text)) else: # https://reuse.software/faq/#multi-licensing for license_id, text in license_data.items(): license_text = get_license_text(config, license_id, text, self.creation_time) - files.append(File(Path('LICENSES', f'{license_id}.txt'), license_text)) + files.append(File(Path("LICENSES", f"{license_id}.txt"), license_text)) - if config['args']['cli']: + if config["args"]["cli"]: from hatch.template import files_feature_cli files.extend(find_template_files(files_feature_cli)) - if self.plugin_config['tests']: + if self.plugin_config["tests"]: from hatch.template import files_feature_tests files.extend(find_template_files(files_feature_tests)) - if self.plugin_config['ci']: + if self.plugin_config["ci"]: from hatch.template import files_feature_ci files.extend(find_template_files(files_feature_ci)) @@ -107,23 +107,23 @@ def get_files(self, config): return files def finalize_files(self, config, files): - if config['licenses']['headers'] and config['license_data']: + if config["licenses"]["headers"] and config["license_data"]: for template_file in files: - if template_file.path.name.endswith('.py'): - template_file.contents = config['license_header'] + template_file.contents + if template_file.path.name.endswith(".py"): + template_file.contents = config["license_header"] + template_file.contents - if self.plugin_config['src-layout']: + if self.plugin_config["src-layout"]: for template_file in files: - if template_file.path.parts[0] == config['package_name']: - template_file.path = Path('src', template_file.path) + if template_file.path.parts[0] == config["package_name"]: + template_file.path = Path("src", template_file.path) def get_license_text(config, license_id, license_text, creation_time): - if license_id == 'MIT': - license_text = license_text.replace('', f'{creation_time.year}-present', 1) - license_text = license_text.replace('', f'{config["name"]} <{config["email"]}>', 1) - elif license_id == 'BSD-3-Clause': - license_text = license_text.replace('', f'{creation_time.year}-present', 1) - license_text = license_text.replace('', f'{config["name"]} <{config["email"]}>', 1) - - return f'{license_text.rstrip()}\n' + if license_id == "MIT": + license_text = license_text.replace("", f"{creation_time.year}-present", 1) + license_text = license_text.replace("", f"{config['name']} <{config['email']}>", 1) + elif license_id == "BSD-3-Clause": + license_text = license_text.replace("", f"{creation_time.year}-present", 1) + license_text = license_text.replace("", f"{config['name']} <{config['email']}>", 1) + + return f"{license_text.rstrip()}\n" diff --git a/src/hatch/template/files_default.py b/src/hatch/template/files_default.py index 063a9e07b..f1173b0b6 100644 --- a/src/hatch/template/files_default.py +++ b/src/hatch/template/files_default.py @@ -8,7 +8,7 @@ def __init__( template_config: dict, plugin_config: dict, # noqa: ARG002 ): - super().__init__(Path(template_config['package_name'], '__init__.py'), '') + super().__init__(Path(template_config["package_name"], "__init__.py"), "") class MetadataFile(File): @@ -17,7 +17,7 @@ def __init__( template_config: dict, plugin_config: dict, # noqa: ARG002 ): - super().__init__(Path(template_config['package_name'], '__about__.py'), '__version__ = "0.0.1"\n') + super().__init__(Path(template_config["package_name"], "__about__.py"), '__version__ = "0.0.1"\n') class Readme(File): @@ -45,27 +45,27 @@ def __init__( template_config: dict, plugin_config: dict, # noqa: ARG002 ): - extra_badges = '' - extra_toc = '' + extra_badges = "" + extra_toc = "" - license_info = '' - if template_config['license_data']: - extra_toc += '- [License](#license)\n' + license_info = "" + if template_config["license_data"]: + extra_toc += "- [License](#license)\n" license_info += ( f"\n\n## License\n\n`{template_config['project_name_normalized']}` is distributed under the terms of " ) - license_data = template_config['license_data'] + license_data = template_config["license_data"] if len(license_data) == 1: license_id = next(iter(license_data)) - license_info += f'the [{license_id}](https://spdx.org/licenses/{license_id}.html) license.' + license_info += f"the [{license_id}](https://spdx.org/licenses/{license_id}.html) license." else: - license_info += 'any of the following licenses:\n' + license_info += "any of the following licenses:\n" for license_id in sorted(license_data): - license_info += f'\n- [{license_id}](https://spdx.org/licenses/{license_id}.html)' + license_info += f"\n- [{license_id}](https://spdx.org/licenses/{license_id}.html)" super().__init__( - Path(template_config['readme_file_path']), + Path(template_config["readme_file_path"]), self.TEMPLATE.format( extra_badges=extra_badges, extra_toc=extra_toc, license_info=license_info, **template_config ), @@ -110,40 +110,40 @@ class PyProject(File): def __init__(self, template_config: dict, plugin_config: dict): template_config = dict(template_config) - template_config['name'] = repr(template_config['name'])[1:-1] + template_config["name"] = repr(template_config["name"])[1:-1] - project_url_data = '' + project_url_data = "" project_urls = ( - plugin_config['project_urls'] - if 'project_urls' in plugin_config + plugin_config["project_urls"] + if "project_urls" in plugin_config else { - 'Documentation': 'https://github.com/{name}/{project_name_normalized}#readme', - 'Issues': 'https://github.com/{name}/{project_name_normalized}/issues', - 'Source': 'https://github.com/{name}/{project_name_normalized}', + "Documentation": "https://github.com/{name}/{project_name_normalized}#readme", + "Issues": "https://github.com/{name}/{project_name_normalized}/issues", + "Source": "https://github.com/{name}/{project_name_normalized}", } ) if project_urls: for label, url in project_urls.items(): - normalized_label = f'"{label}"' if ' ' in label else label + normalized_label = f'"{label}"' if " " in label else label project_url_data += f'\n{normalized_label} = "{url.format(**template_config)}"' - dependency_data = '[' - if template_config['dependencies']: - for dependency in sorted(template_config['dependencies']): + dependency_data = "[" + if template_config["dependencies"]: + for dependency in sorted(template_config["dependencies"]): dependency_data += f'\n "{dependency}",\n' - dependency_data += ']' + dependency_data += "]" - cli_scripts = '' - if template_config['args']['cli']: + cli_scripts = "" + if template_config["args"]["cli"]: cli_scripts = f""" [project.scripts] -{template_config['project_name_normalized']} = "{template_config['package_name']}.cli:{template_config['package_name']}"\ +{template_config["project_name_normalized"]} = "{template_config["package_name"]}.cli:{template_config["package_name"]}"\ """ - tests_section = '' - if plugin_config['tests']: - package_location = 'src/' if plugin_config['src-layout'] else '' + tests_section = "" + if plugin_config["tests"]: + package_location = "src/" if plugin_config["src-layout"] else "" tests_section = f""" [tool.hatch.envs.types] @@ -151,19 +151,19 @@ def __init__(self, template_config: dict, plugin_config: dict): "mypy>=1.0.0", ] [tool.hatch.envs.types.scripts] -check = "mypy --install-types --non-interactive {{args:{package_location}{template_config['package_name']} tests}}" +check = "mypy --install-types --non-interactive {{args:{package_location}{template_config["package_name"]} tests}}" [tool.coverage.run] -source_pkgs = ["{template_config['package_name']}", "tests"] +source_pkgs = ["{template_config["package_name"]}", "tests"] branch = true parallel = true omit = [ - "{package_location}{template_config['package_name']}/__about__.py", + "{package_location}{template_config["package_name"]}/__about__.py", ] [tool.coverage.paths] -{template_config['package_name']} = ["{package_location}{template_config['package_name']}", "*/{template_config['project_name_normalized']}/{package_location}{template_config['package_name']}"] -tests = ["tests", "*/{template_config['project_name_normalized']}/tests"] +{template_config["package_name"]} = ["{package_location}{template_config["package_name"]}", "*/{template_config["project_name_normalized"]}/{package_location}{template_config["package_name"]}"] +tests = ["tests", "*/{template_config["project_name_normalized"]}/tests"] [tool.coverage.report] exclude_lines = [ @@ -173,7 +173,7 @@ def __init__(self, template_config: dict, plugin_config: dict): ]""" super().__init__( - Path('pyproject.toml'), + Path("pyproject.toml"), self.TEMPLATE.format( project_url_data=project_url_data, dependency_data=dependency_data, diff --git a/src/hatch/template/files_feature_ci.py b/src/hatch/template/files_feature_ci.py index d7dfee9d9..126b681ce 100644 --- a/src/hatch/template/files_feature_ci.py +++ b/src/hatch/template/files_feature_ci.py @@ -53,4 +53,4 @@ def __init__( template_config: dict, # noqa: ARG002 plugin_config: dict, # noqa: ARG002 ): - super().__init__(Path('.github', 'workflows', 'test.yml'), self.TEMPLATE) + super().__init__(Path(".github", "workflows", "test.yml"), self.TEMPLATE) diff --git a/src/hatch/template/files_feature_cli.py b/src/hatch/template/files_feature_cli.py index 6786dffd0..6267701df 100644 --- a/src/hatch/template/files_feature_cli.py +++ b/src/hatch/template/files_feature_cli.py @@ -17,7 +17,7 @@ def __init__( template_config: dict, plugin_config: dict, # noqa: ARG002 ): - super().__init__(Path(template_config['package_name'], '__main__.py'), self.TEMPLATE.format(**template_config)) + super().__init__(Path(template_config["package_name"], "__main__.py"), self.TEMPLATE.format(**template_config)) class CommandLinePackage(File): @@ -39,5 +39,5 @@ def __init__( plugin_config: dict, # noqa: ARG002 ): super().__init__( - Path(template_config['package_name'], 'cli', '__init__.py'), self.TEMPLATE.format(**template_config) + Path(template_config["package_name"], "cli", "__init__.py"), self.TEMPLATE.format(**template_config) ) diff --git a/src/hatch/template/files_feature_tests.py b/src/hatch/template/files_feature_tests.py index 6637355b5..37a5a483f 100644 --- a/src/hatch/template/files_feature_tests.py +++ b/src/hatch/template/files_feature_tests.py @@ -8,4 +8,4 @@ def __init__( template_config: dict, # noqa: ARG002 plugin_config: dict, # noqa: ARG002 ): - super().__init__(Path('tests', '__init__.py')) + super().__init__(Path("tests", "__init__.py")) diff --git a/src/hatch/template/plugin/interface.py b/src/hatch/template/plugin/interface.py index 2a361c666..62c076ec9 100644 --- a/src/hatch/template/plugin/interface.py +++ b/src/hatch/template/plugin/interface.py @@ -1,5 +1,5 @@ class TemplateInterface: - PLUGIN_NAME = '' + PLUGIN_NAME = "" PRIORITY = 100 def __init__(self, plugin_config: dict, cache_dir, creation_time): diff --git a/src/hatch/utils/ci.py b/src/hatch/utils/ci.py index b58ae900b..c261434a9 100644 --- a/src/hatch/utils/ci.py +++ b/src/hatch/utils/ci.py @@ -2,4 +2,4 @@ def running_in_ci() -> bool: - return any(os.environ.get(env_var) in {'true', '1'} for env_var in ('CI', 'GITHUB_ACTIONS')) + return any(os.environ.get(env_var) in {"true", "1"} for env_var in ("CI", "GITHUB_ACTIONS")) diff --git a/src/hatch/utils/dep.py b/src/hatch/utils/dep.py index e9964a3bf..e8456d6b6 100644 --- a/src/hatch/utils/dep.py +++ b/src/hatch/utils/dep.py @@ -21,13 +21,13 @@ def get_normalized_dependencies(requirements: list[Requirement]) -> list[str]: def hash_dependencies(requirements: list[Requirement]) -> str: from hashlib import sha256 - data = ''.join( + data = "".join( sorted( # Internal spacing is ignored by PEP 440 - normalized_dependency.replace(' ', '') + normalized_dependency.replace(" ", "") for normalized_dependency in {get_normalized_dependency(req) for req in requirements} ) - ).encode('utf-8') + ).encode("utf-8") return sha256(data).hexdigest() diff --git a/src/hatch/utils/env.py b/src/hatch/utils/env.py index adfdf45a2..30524e171 100644 --- a/src/hatch/utils/env.py +++ b/src/hatch/utils/env.py @@ -8,7 +8,7 @@ class PythonInfo: - def __init__(self, platform: Platform, executable: str = 'python') -> None: + def __init__(self, platform: Platform, executable: str = "python") -> None: self.platform = platform self.executable = executable @@ -20,24 +20,24 @@ def __init__(self, platform: Platform, executable: str = 'python') -> None: def dep_check_data(self) -> dict[str, Any]: if self.__dep_check_data is None: process = self.platform.check_command( - [self.executable, '-W', 'ignore', '-'], capture_output=True, input=DEP_CHECK_DATA_SCRIPT + [self.executable, "-W", "ignore", "-"], capture_output=True, input=DEP_CHECK_DATA_SCRIPT ) - self.__dep_check_data = literal_eval(process.stdout.strip().decode('utf-8')) + self.__dep_check_data = literal_eval(process.stdout.strip().decode("utf-8")) return self.__dep_check_data @property def environment(self) -> dict[str, str]: if self.__environment is None: - self.__environment = self.dep_check_data['environment'] + self.__environment = self.dep_check_data["environment"] return self.__environment @property def sys_path(self) -> list[str]: if self.__sys_path is None: - self.__sys_path = self.dep_check_data['sys_path'] + self.__sys_path = self.dep_check_data["sys_path"] return self.__sys_path diff --git a/src/hatch/utils/fs.py b/src/hatch/utils/fs.py index 61b4e3855..e84e684c7 100644 --- a/src/hatch/utils/fs.py +++ b/src/hatch/utils/fs.py @@ -16,7 +16,7 @@ # There is special recognition in Mypy for `sys.platform`, not `os.name` # https://github.com/python/cpython/blob/09d7319bfe0006d9aa3fc14833b69c24ccafdca6/Lib/pathlib.py#L957 -if sys.platform == 'win32': +if sys.platform == "win32": _PathBase = pathlib.WindowsPath else: _PathBase = pathlib.PosixPath @@ -24,10 +24,10 @@ disk_sync = os.fsync # https://mjtsai.com/blog/2022/02/17/apple-ssd-benchmarks-and-f_fullsync/ # https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/fsync.2.html -if sys.platform == 'darwin': +if sys.platform == "darwin": import fcntl - if hasattr(fcntl, 'F_FULLFSYNC'): + if hasattr(fcntl, "F_FULLFSYNC"): def disk_sync(fd: FileDescriptorLike) -> None: fcntl.fcntl(fd, fcntl.F_FULLFSYNC) @@ -40,11 +40,11 @@ def long_id(self) -> str: from hashlib import sha256 path = str(self) - if sys.platform == 'win32' or sys.platform == 'darwin': + if sys.platform == "win32" or sys.platform == "darwin": path = path.casefold() - digest = sha256(path.encode('utf-8')).digest() - return urlsafe_b64encode(digest).decode('utf-8') + digest = sha256(path.encode("utf-8")).digest() + return urlsafe_b64encode(digest).decode("utf-8") @cached_property def id(self) -> str: diff --git a/src/hatch/utils/network.py b/src/hatch/utils/network.py index 718300bf7..d73d53f53 100644 --- a/src/hatch/utils/network.py +++ b/src/hatch/utils/network.py @@ -43,8 +43,8 @@ def streaming_response(*args: Any, **kwargs: Any) -> Generator[httpx.Response, N def download_file(path: Path, *args: Any, **kwargs: Any) -> None: - kwargs.setdefault('timeout', DEFAULT_TIMEOUT) + kwargs.setdefault("timeout", DEFAULT_TIMEOUT) - with path.open(mode='wb', buffering=0) as f, streaming_response('GET', *args, **kwargs) as response: + with path.open(mode="wb", buffering=0) as f, streaming_response("GET", *args, **kwargs) as response: for chunk in response.iter_bytes(16384): f.write(chunk) diff --git a/src/hatch/utils/platform.py b/src/hatch/utils/platform.py index 2ebb0eddd..fbd2cf5c6 100644 --- a/src/hatch/utils/platform.py +++ b/src/hatch/utils/platform.py @@ -23,7 +23,7 @@ def get_platform_name() -> str: def normalize_platform_name(platform_name: str) -> str: platform_name = platform_name.lower() - return 'macos' if platform_name == 'darwin' else platform_name + return "macos" if platform_name == "darwin" else platform_name class Platform: @@ -81,7 +81,7 @@ def _run_command_integrated( ) -> CompletedProcess: with self.capture_process(command, shell=shell, **kwargs) as process: for line in self.stream_process_output(process): - self.__display_func(line, end='') + self.__display_func(line, end="") stdout, stderr = process.communicate() @@ -94,7 +94,7 @@ def run_command(self, command: str | list[str], *, shell: bool = False, **kwargs with the command first being [properly formatted](utilities.md#hatch.utils.platform.Platform.format_for_subprocess). """ - if self.displaying_status and not kwargs.get('capture_output'): + if self.displaying_status and not kwargs.get("capture_output"): return self._run_command_integrated(command, shell=shell, **kwargs) self.populate_default_popen_kwargs(kwargs, shell=shell) @@ -117,17 +117,17 @@ def check_command_output(self, command: str | list[str], *, shell: bool = False, [capture_process](utilities.md#hatch.utils.platform.Platform.capture_process), but non-zero exit codes will gracefully end program execution. """ - kwargs.setdefault('stdout', self.modules.subprocess.PIPE) - kwargs.setdefault('stderr', self.modules.subprocess.STDOUT) + kwargs.setdefault("stdout", self.modules.subprocess.PIPE) + kwargs.setdefault("stderr", self.modules.subprocess.STDOUT) self.populate_default_popen_kwargs(kwargs, shell=shell) process = self.modules.subprocess.run(self.format_for_subprocess(command, shell=shell), shell=shell, **kwargs) if process.returncode: # Callers might not want to merge both streams so try stderr first - self.__display_func((process.stderr or process.stdout).decode('utf-8')) + self.__display_func((process.stderr or process.stdout).decode("utf-8")) self.exit_with_code(process.returncode) - return process.stdout.decode('utf-8') + return process.stdout.decode("utf-8") def capture_process(self, command: str | list[str], *, shell: bool = False, **kwargs: Any) -> Popen: """ @@ -149,36 +149,36 @@ def populate_default_popen_kwargs(self, kwargs: dict[str, Any], *, shell: bool) # https://support.apple.com/en-us/HT204899 # https://en.wikipedia.org/wiki/System_Integrity_Protection if ( - 'executable' not in kwargs + "executable" not in kwargs and self.macos and shell - and any(env_var.startswith(('DYLD_', 'LD_')) for env_var in os.environ) + and any(env_var.startswith(("DYLD_", "LD_")) for env_var in os.environ) ): - default_paths = os.environ.get('PATH', os.defpath).split(os.pathsep) + default_paths = os.environ.get("PATH", os.defpath).split(os.pathsep) unprotected_paths = [] for path in default_paths: normalized_path = os.path.normpath(path) if not normalized_path.startswith(( - '/System', - '/usr', - '/bin', - '/sbin', - '/var', - )) or normalized_path.startswith('/usr/local'): + "/System", + "/usr", + "/bin", + "/sbin", + "/var", + )) or normalized_path.startswith("/usr/local"): unprotected_paths.append(path) search_path = os.pathsep.join(unprotected_paths) - for exe_name in ('sh', 'bash', 'zsh', 'fish'): + for exe_name in ("sh", "bash", "zsh", "fish"): executable = self.modules.shutil.which(exe_name, path=search_path) if executable: - kwargs['executable'] = executable + kwargs["executable"] = executable break @staticmethod def stream_process_output(process: Popen) -> Iterable[str]: # To avoid blocking never use a pipe's file descriptor iterator. See https://bugs.python.org/issue3907 - for line in iter(process.stdout.readline, b''): # type: ignore[union-attr] - yield line.decode('utf-8') + for line in iter(process.stdout.readline, b""): # type: ignore[union-attr] + yield line.decode("utf-8") @property def default_shell(self) -> str: @@ -191,9 +191,9 @@ def default_shell(self) -> str: """ if self.__default_shell is None: if self.windows: - self.__default_shell = cast(str, os.environ.get('SHELL', os.environ.get('COMSPEC', 'cmd'))) + self.__default_shell = cast(str, os.environ.get("SHELL", os.environ.get("COMSPEC", "cmd"))) else: - self.__default_shell = cast(str, os.environ.get('SHELL', 'bash')) + self.__default_shell = cast(str, os.environ.get("SHELL", "bash")) return self.__default_shell @property @@ -210,9 +210,9 @@ def join_command_args(self) -> Callable[[list[str]], str]: def format_file_uri(self) -> Callable[[str], str]: if self.__format_file_uri is None: if self.windows: - self.__format_file_uri = lambda p: f'file:///{p}'.replace('\\', '/') + self.__format_file_uri = lambda p: f"file:///{p}".replace("\\", "/") else: - self.__format_file_uri = lambda p: f'file://{p}' + self.__format_file_uri = lambda p: f"file://{p}" return self.__format_file_uri @@ -221,14 +221,14 @@ def windows(self) -> bool: """ Indicates whether Hatch is running on Windows. """ - return self.name == 'windows' + return self.name == "windows" @property def macos(self) -> bool: """ Indicates whether Hatch is running on macOS. """ - return self.name == 'macos' + return self.name == "macos" @property def linux(self) -> bool: @@ -272,7 +272,7 @@ def display_name(self) -> str: - `macOS` """ if self.__display_name is None: - self.__display_name = 'macOS' if self.macos else self.name.capitalize() + self.__display_name = "macOS" if self.macos else self.name.capitalize() return self.__display_name @@ -284,7 +284,7 @@ def home(self) -> Path: if self.__home is None: from hatch.utils.fs import Path - self.__home = Path(os.path.expanduser('~')) + self.__home = Path(os.path.expanduser("~")) return self.__home diff --git a/src/hatch/utils/runner.py b/src/hatch/utils/runner.py index 8c499d007..156e465db 100644 --- a/src/hatch/utils/runner.py +++ b/src/hatch/utils/runner.py @@ -16,7 +16,7 @@ def __init__( force_continue: bool = False, show_code_on_error: bool = False, hide_commands: bool = False, - source: str = 'cmd', + source: str = "cmd", ) -> None: self.env = environment self.shell_commands: list[str] = shell_commands or [] @@ -33,14 +33,14 @@ def add_shell_command(self, command: str | list[str]) -> None: def parse_matrix_variables(specs: tuple[str, ...]) -> dict[str, set[str]]: variables: dict[str, set[str]] = {} for spec in specs: - variable, _, values = spec.partition('=') - if variable == 'py': - variable = 'python' + variable, _, values = spec.partition("=") + if variable == "py": + variable = "python" if variable in variables: raise ValueError(variable) - variables[variable] = set(values.split(',')) if values else set() + variables[variable] = set(values.split(",")) if values else set() return variables diff --git a/src/hatch/utils/shells.py b/src/hatch/utils/shells.py index b64c983c7..c646a88a4 100644 --- a/src/hatch/utils/shells.py +++ b/src/hatch/utils/shells.py @@ -28,71 +28,71 @@ def __init__(self, environment: EnvironmentInterface) -> None: self.environment = environment def enter_cmd(self, path: str, args: Iterable[str], exe_dir: Path) -> None: # noqa: ARG002 - self.environment.platform.exit_with_command([path or 'cmd', '/k', str(exe_dir / 'activate.bat')]) + self.environment.platform.exit_with_command([path or "cmd", "/k", str(exe_dir / "activate.bat")]) def enter_powershell(self, path: str, args: Iterable[str], exe_dir: Path) -> None: # noqa: ARG002 self.environment.platform.exit_with_command([ - path or 'powershell', - '-executionpolicy', - 'bypass', - '-NoExit', - '-NoLogo', - '-File', - str(exe_dir / 'activate.ps1'), + path or "powershell", + "-executionpolicy", + "bypass", + "-NoExit", + "-NoLogo", + "-File", + str(exe_dir / "activate.ps1"), ]) def enter_pwsh(self, path: str, args: Iterable[str], exe_dir: Path) -> None: - self.enter_powershell(path or 'pwsh', args, exe_dir) + self.enter_powershell(path or "pwsh", args, exe_dir) def enter_xonsh(self, path: str, args: Iterable[str], exe_dir: Path) -> None: if self.environment.platform.windows: with self.environment: self.environment.platform.exit_with_command([ - path or 'xonsh', - *(args or ['-i']), - '-D', - f'VIRTUAL_ENV={exe_dir.parent.name}', + path or "xonsh", + *(args or ["-i"]), + "-D", + f"VIRTUAL_ENV={exe_dir.parent.name}", ]) else: self.spawn_linux_shell( - path or 'xonsh', - [*(args or ['-i']), '-D', f'VIRTUAL_ENV={exe_dir.parent.name}'], + path or "xonsh", + [*(args or ["-i"]), "-D", f"VIRTUAL_ENV={exe_dir.parent.name}"], # Just in case pyenv works with xonsh, supersede it. - callback=lambda terminal: terminal.sendline(f'$PATH.insert(0, {str(exe_dir)!r})'), + callback=lambda terminal: terminal.sendline(f"$PATH.insert(0, {str(exe_dir)!r})"), ) def enter_bash(self, path: str, args: Iterable[str], exe_dir: Path) -> None: if self.environment.platform.windows: self.environment.platform.exit_with_command([ - path or 'bash', - '--init-file', - exe_dir / 'activate', - *(args or ['-i']), + path or "bash", + "--init-file", + exe_dir / "activate", + *(args or ["-i"]), ]) else: - self.spawn_linux_shell(path or 'bash', args or ['-i'], script=exe_dir / 'activate') + self.spawn_linux_shell(path or "bash", args or ["-i"], script=exe_dir / "activate") def enter_fish(self, path: str, args: Iterable[str], exe_dir: Path) -> None: - self.spawn_linux_shell(path or 'fish', args or ['-i'], script=exe_dir / 'activate.fish') + self.spawn_linux_shell(path or "fish", args or ["-i"], script=exe_dir / "activate.fish") def enter_zsh(self, path: str, args: Iterable[str], exe_dir: Path) -> None: - self.spawn_linux_shell(path or 'zsh', args or ['-i'], script=exe_dir / 'activate') + self.spawn_linux_shell(path or "zsh", args or ["-i"], script=exe_dir / "activate") def enter_ash(self, path: str, args: Iterable[str], exe_dir: Path) -> None: - self.spawn_linux_shell(path or 'ash', args or ['-i'], script=exe_dir / 'activate') + self.spawn_linux_shell(path or "ash", args or ["-i"], script=exe_dir / "activate") def enter_nu(self, path: str, args: Iterable[str], exe_dir: Path) -> None: # noqa: ARG002 - executable = path or 'nu' - activation_script = exe_dir / 'activate.nu' - self.environment.platform.exit_with_command([executable, '-e', f'overlay use {str(activation_script)!r}']) + executable = path or "nu" + activation_script = exe_dir / "activate.nu" + self.environment.platform.exit_with_command([executable, "-e", f"overlay use {str(activation_script)!r}"]) def enter_tcsh(self, path: str, args: Iterable[str], exe_dir: Path) -> None: - self.spawn_linux_shell(path or 'tcsh', args or ['-i'], script=exe_dir / 'activate.csh') + self.spawn_linux_shell(path or "tcsh", args or ["-i"], script=exe_dir / "activate.csh") def enter_csh(self, path: str, args: Iterable[str], exe_dir: Path) -> None: - self.spawn_linux_shell(path or 'csh', args or ['-i'], script=exe_dir / 'activate.csh') + self.spawn_linux_shell(path or "csh", args or ["-i"], script=exe_dir / "activate.csh") - if sys.platform == 'win32': + if sys.platform == "win32": def spawn_linux_shell( self, diff --git a/src/hatch/utils/toml.py b/src/hatch/utils/toml.py index 23893bef7..0d061789f 100644 --- a/src/hatch/utils/toml.py +++ b/src/hatch/utils/toml.py @@ -14,5 +14,5 @@ def load_toml_data(data: str) -> dict[str, Any]: def load_toml_file(path: str) -> dict[str, Any]: - with open(path, encoding='utf-8') as f: + with open(path, encoding="utf-8") as f: return tomllib.loads(f.read()) diff --git a/src/hatch/venv/core.py b/src/hatch/venv/core.py index d1303f031..d8c6379ed 100644 --- a/src/hatch/venv/core.py +++ b/src/hatch/venv/core.py @@ -8,7 +8,7 @@ class VirtualEnv: - IGNORED_ENV_VARS = ('__PYVENV_LAUNCHER__', 'PYTHONHOME') + IGNORED_ENV_VARS = ("__PYVENV_LAUNCHER__", "PYTHONHOME") def __init__(self, directory, platform, verbosity=0): self.directory = directory @@ -20,15 +20,15 @@ def __init__(self, directory, platform, verbosity=0): self._executables_directory = None def activate(self): - self._env_vars_to_restore['VIRTUAL_ENV'] = os.environ.pop('VIRTUAL_ENV', None) - os.environ['VIRTUAL_ENV'] = str(self.directory) + self._env_vars_to_restore["VIRTUAL_ENV"] = os.environ.pop("VIRTUAL_ENV", None) + os.environ["VIRTUAL_ENV"] = str(self.directory) - old_path = os.environ.pop('PATH', None) - self._env_vars_to_restore['PATH'] = old_path + old_path = os.environ.pop("PATH", None) + self._env_vars_to_restore["PATH"] = old_path if old_path is None: - os.environ['PATH'] = f'{self.executables_directory}{os.pathsep}{os.defpath}' + os.environ["PATH"] = f"{self.executables_directory}{os.pathsep}{os.defpath}" else: - os.environ['PATH'] = f'{self.executables_directory}{os.pathsep}{old_path}' + os.environ["PATH"] = f"{self.executables_directory}{os.pathsep}{old_path}" for env_var in self.IGNORED_ENV_VARS: self._env_vars_to_restore[env_var] = os.environ.pop(env_var, None) @@ -48,10 +48,10 @@ def create(self, python, *, allow_system_packages=False): self.directory.ensure_parent_dir_exists() - command = [str(self.directory), '--no-download', '--no-periodic-update', '--python', python] + command = [str(self.directory), "--no-download", "--no-periodic-update", "--python", python] if allow_system_packages: - command.append('--system-site-packages') + command.append("--system-site-packages") # Decrease verbosity since the virtualenv CLI defaults to something like +2 verbosity add_verbosity_flag(command, self.verbosity, adjustment=-1) @@ -67,27 +67,27 @@ def exists(self): @property def executables_directory(self): if self._executables_directory is None: - exe_dir = self.directory / ('Scripts' if self.platform.windows else 'bin') + exe_dir = self.directory / ("Scripts" if self.platform.windows else "bin") if exe_dir.is_dir(): self._executables_directory = exe_dir # PyPy elif self.platform.windows: - exe_dir = self.directory / 'bin' + exe_dir = self.directory / "bin" if exe_dir.is_dir(): self._executables_directory = exe_dir else: - msg = f'Unable to locate executables directory within: {self.directory}' + msg = f"Unable to locate executables directory within: {self.directory}" raise OSError(msg) # Debian - elif (self.directory / 'local').is_dir(): # no cov - exe_dir = self.directory / 'local' / 'bin' + elif (self.directory / "local").is_dir(): # no cov + exe_dir = self.directory / "local" / "bin" if exe_dir.is_dir(): self._executables_directory = exe_dir else: - msg = f'Unable to locate executables directory within: {self.directory}' + msg = f"Unable to locate executables directory within: {self.directory}" raise OSError(msg) else: - msg = f'Unable to locate executables directory within: {self.directory}' + msg = f"Unable to locate executables directory within: {self.directory}" raise OSError(msg) return self._executables_directory @@ -131,9 +131,9 @@ def __exit__(self, exc_type, exc_value, traceback): class UVVirtualEnv(VirtualEnv): def create(self, python, *, allow_system_packages=False): - command = [os.environ.get('HATCH_UV', 'uv'), 'venv', str(self.directory), '--python', python] + command = [os.environ.get("HATCH_UV", "uv"), "venv", str(self.directory), "--python", python] if allow_system_packages: - command.append('--system-site-packages') + command.append("--system-site-packages") add_verbosity_flag(command, self.verbosity, adjustment=-1) self.platform.run_command(command) diff --git a/src/hatch/venv/utils.py b/src/hatch/venv/utils.py index 01c703e0c..5191ef2e0 100644 --- a/src/hatch/venv/utils.py +++ b/src/hatch/venv/utils.py @@ -4,4 +4,4 @@ def get_random_venv_name(): # Will be length 4 - return urlsafe_b64encode(urandom(3)).decode('ascii') + return urlsafe_b64encode(urandom(3)).decode("ascii") diff --git a/tests/backend/builders/hooks/test_custom.py b/tests/backend/builders/hooks/test_custom.py index 1fc918c13..48c1b1616 100644 --- a/tests/backend/builders/hooks/test_custom.py +++ b/tests/backend/builders/hooks/test_custom.py @@ -7,24 +7,24 @@ def test_no_path(isolation): - config = {'path': ''} + config = {"path": ""} - with pytest.raises(ValueError, match='Option `path` for build hook `custom` must not be empty if defined'): - CustomBuildHook(str(isolation), config, None, None, '', '') + with pytest.raises(ValueError, match="Option `path` for build hook `custom` must not be empty if defined"): + CustomBuildHook(str(isolation), config, None, None, "", "") def test_path_not_string(isolation): - config = {'path': 3} + config = {"path": 3} - with pytest.raises(TypeError, match='Option `path` for build hook `custom` must be a string'): - CustomBuildHook(str(isolation), config, None, None, '', '') + with pytest.raises(TypeError, match="Option `path` for build hook `custom` must be a string"): + CustomBuildHook(str(isolation), config, None, None, "", "") def test_nonexistent(isolation): - config = {'path': 'test.py'} + config = {"path": "test.py"} - with pytest.raises(OSError, match='Build script does not exist: test.py'): - CustomBuildHook(str(isolation), config, None, None, '', '') + with pytest.raises(OSError, match="Build script does not exist: test.py"): + CustomBuildHook(str(isolation), config, None, None, "", "") def test_default(temp_dir, helpers): @@ -44,15 +44,15 @@ def foo(self): ) with temp_dir.as_cwd(): - hook = CustomBuildHook(str(temp_dir), config, None, None, '', '') + hook = CustomBuildHook(str(temp_dir), config, None, None, "", "") - assert hook.foo() == ('custom', str(temp_dir)) + assert hook.foo() == ("custom", str(temp_dir)) def test_explicit_path(temp_dir, helpers): - config = {'path': f'foo/{DEFAULT_BUILD_SCRIPT}'} + config = {"path": f"foo/{DEFAULT_BUILD_SCRIPT}"} - file_path = temp_dir / 'foo' / DEFAULT_BUILD_SCRIPT + file_path = temp_dir / "foo" / DEFAULT_BUILD_SCRIPT file_path.ensure_parent_dir_exists() file_path.write_text( helpers.dedent( @@ -67,15 +67,15 @@ def foo(self): ) with temp_dir.as_cwd(): - hook = CustomBuildHook(str(temp_dir), config, None, None, '', '') + hook = CustomBuildHook(str(temp_dir), config, None, None, "", "") - assert hook.foo() == ('custom', str(temp_dir)) + assert hook.foo() == ("custom", str(temp_dir)) def test_no_subclass(temp_dir, helpers): - config = {'path': f'foo/{DEFAULT_BUILD_SCRIPT}'} + config = {"path": f"foo/{DEFAULT_BUILD_SCRIPT}"} - file_path = temp_dir / 'foo' / DEFAULT_BUILD_SCRIPT + file_path = temp_dir / "foo" / DEFAULT_BUILD_SCRIPT file_path.ensure_parent_dir_exists() file_path.write_text( helpers.dedent( @@ -91,10 +91,13 @@ class CustomHook: ) ) - with pytest.raises( - ValueError, - match=re.escape( - f'Unable to find a subclass of `BuildHookInterface` in `foo/{DEFAULT_BUILD_SCRIPT}`: {temp_dir}' + with ( + pytest.raises( + ValueError, + match=re.escape( + f"Unable to find a subclass of `BuildHookInterface` in `foo/{DEFAULT_BUILD_SCRIPT}`: {temp_dir}" + ), ), - ), temp_dir.as_cwd(): - CustomBuildHook(str(temp_dir), config, None, None, '', '') + temp_dir.as_cwd(), + ): + CustomBuildHook(str(temp_dir), config, None, None, "", "") diff --git a/tests/backend/builders/hooks/test_version.py b/tests/backend/builders/hooks/test_version.py index b03500292..3588cf83d 100644 --- a/tests/backend/builders/hooks/test_version.py +++ b/tests/backend/builders/hooks/test_version.py @@ -8,65 +8,65 @@ class TestConfigPath: def test_correct(self, isolation): - config = {'path': 'foo/bar.py'} - hook = VersionBuildHook(str(isolation), config, None, None, '', '') + config = {"path": "foo/bar.py"} + hook = VersionBuildHook(str(isolation), config, None, None, "", "") - assert hook.config_path == hook.config_path == 'foo/bar.py' + assert hook.config_path == hook.config_path == "foo/bar.py" def test_missing(self, isolation): - config = {'path': ''} - hook = VersionBuildHook(str(isolation), config, None, None, '', '') + config = {"path": ""} + hook = VersionBuildHook(str(isolation), config, None, None, "", "") - with pytest.raises(ValueError, match='Option `path` for build hook `version` is required'): + with pytest.raises(ValueError, match="Option `path` for build hook `version` is required"): _ = hook.config_path def test_not_string(self, isolation): - config = {'path': 9000} - hook = VersionBuildHook(str(isolation), config, None, None, '', '') + config = {"path": 9000} + hook = VersionBuildHook(str(isolation), config, None, None, "", "") - with pytest.raises(TypeError, match='Option `path` for build hook `version` must be a string'): + with pytest.raises(TypeError, match="Option `path` for build hook `version` must be a string"): _ = hook.config_path class TestConfigTemplate: def test_correct(self, isolation): - config = {'template': 'foo'} - hook = VersionBuildHook(str(isolation), config, None, None, '', '') + config = {"template": "foo"} + hook = VersionBuildHook(str(isolation), config, None, None, "", "") - assert hook.config_template == hook.config_template == 'foo' + assert hook.config_template == hook.config_template == "foo" def test_not_string(self, isolation): - config = {'template': 9000} - hook = VersionBuildHook(str(isolation), config, None, None, '', '') + config = {"template": 9000} + hook = VersionBuildHook(str(isolation), config, None, None, "", "") - with pytest.raises(TypeError, match='Option `template` for build hook `version` must be a string'): + with pytest.raises(TypeError, match="Option `template` for build hook `version` must be a string"): _ = hook.config_template class TestConfigPattern: def test_correct(self, isolation): - config = {'pattern': 'foo'} - hook = VersionBuildHook(str(isolation), config, None, None, '', '') + config = {"pattern": "foo"} + hook = VersionBuildHook(str(isolation), config, None, None, "", "") - assert hook.config_pattern == hook.config_pattern == 'foo' + assert hook.config_pattern == hook.config_pattern == "foo" def test_not_string(self, isolation): - config = {'pattern': 9000} - hook = VersionBuildHook(str(isolation), config, None, None, '', '') + config = {"pattern": 9000} + hook = VersionBuildHook(str(isolation), config, None, None, "", "") - with pytest.raises(TypeError, match='Option `pattern` for build hook `version` must be a string'): + with pytest.raises(TypeError, match="Option `pattern` for build hook `version` must be a string"): _ = hook.config_pattern class TestTemplate: def test_default(self, temp_dir, helpers): - config = {'path': 'baz.py'} + config = {"path": "baz.py"} metadata = ProjectMetadata( str(temp_dir), PluginManager(), { - 'project': {'name': 'foo', 'dynamic': ['version']}, - 'tool': {'hatch': {'metadata': {'hooks': {'custom': {}}}}}, + "project": {"name": "foo", "dynamic": ["version"]}, + "tool": {"hatch": {"metadata": {"hooks": {"custom": {}}}}}, }, ) @@ -83,11 +83,11 @@ def update(self, metadata): ) ) - build_data = {'artifacts': []} - hook = VersionBuildHook(str(temp_dir), config, None, metadata, '', '') + build_data = {"artifacts": []} + hook = VersionBuildHook(str(temp_dir), config, None, metadata, "", "") hook.initialize([], build_data) - expected_file = temp_dir / 'baz.py' + expected_file = temp_dir / "baz.py" assert expected_file.is_file() assert expected_file.read_text() == helpers.dedent( """ @@ -97,16 +97,16 @@ def update(self, metadata): __version__ = VERSION = '1.2.3' """ ) - assert build_data['artifacts'] == ['/baz.py'] + assert build_data["artifacts"] == ["/baz.py"] def test_create_necessary_directories(self, temp_dir, helpers): - config = {'path': 'bar/baz.py'} + config = {"path": "bar/baz.py"} metadata = ProjectMetadata( str(temp_dir), PluginManager(), { - 'project': {'name': 'foo', 'dynamic': ['version']}, - 'tool': {'hatch': {'metadata': {'hooks': {'custom': {}}}}}, + "project": {"name": "foo", "dynamic": ["version"]}, + "tool": {"hatch": {"metadata": {"hooks": {"custom": {}}}}}, }, ) @@ -123,11 +123,11 @@ def update(self, metadata): ) ) - build_data = {'artifacts': []} - hook = VersionBuildHook(str(temp_dir), config, None, metadata, '', '') + build_data = {"artifacts": []} + hook = VersionBuildHook(str(temp_dir), config, None, metadata, "", "") hook.initialize([], build_data) - expected_file = temp_dir / 'bar' / 'baz.py' + expected_file = temp_dir / "bar" / "baz.py" assert expected_file.is_file() assert expected_file.read_text() == helpers.dedent( """ @@ -137,16 +137,16 @@ def update(self, metadata): __version__ = VERSION = '1.2.3' """ ) - assert build_data['artifacts'] == ['/bar/baz.py'] + assert build_data["artifacts"] == ["/bar/baz.py"] def test_custom(self, temp_dir, helpers): - config = {'path': 'baz.py', 'template': 'VER = {version!r}\n'} + config = {"path": "baz.py", "template": "VER = {version!r}\n"} metadata = ProjectMetadata( str(temp_dir), PluginManager(), { - 'project': {'name': 'foo', 'dynamic': ['version']}, - 'tool': {'hatch': {'metadata': {'hooks': {'custom': {}}}}}, + "project": {"name": "foo", "dynamic": ["version"]}, + "tool": {"hatch": {"metadata": {"hooks": {"custom": {}}}}}, }, ) @@ -163,29 +163,29 @@ def update(self, metadata): ) ) - build_data = {'artifacts': []} - hook = VersionBuildHook(str(temp_dir), config, None, metadata, '', '') + build_data = {"artifacts": []} + hook = VersionBuildHook(str(temp_dir), config, None, metadata, "", "") hook.initialize([], build_data) - expected_file = temp_dir / 'baz.py' + expected_file = temp_dir / "baz.py" assert expected_file.is_file() assert expected_file.read_text() == helpers.dedent( """ VER = '1.2.3' """ ) - assert build_data['artifacts'] == ['/baz.py'] + assert build_data["artifacts"] == ["/baz.py"] class TestPattern: def test_default(self, temp_dir, helpers): - config = {'path': 'baz.py', 'pattern': True} + config = {"path": "baz.py", "pattern": True} metadata = ProjectMetadata( str(temp_dir), PluginManager(), { - 'project': {'name': 'foo', 'dynamic': ['version']}, - 'tool': {'hatch': {'metadata': {'hooks': {'custom': {}}}}}, + "project": {"name": "foo", "dynamic": ["version"]}, + "tool": {"hatch": {"metadata": {"hooks": {"custom": {}}}}}, }, ) @@ -201,7 +201,7 @@ def update(self, metadata): """ ) ) - version_file = temp_dir / 'baz.py' + version_file = temp_dir / "baz.py" version_file.write_text( helpers.dedent( """ @@ -210,8 +210,8 @@ def update(self, metadata): ) ) - build_data = {'artifacts': []} - hook = VersionBuildHook(str(temp_dir), config, None, metadata, '', '') + build_data = {"artifacts": []} + hook = VersionBuildHook(str(temp_dir), config, None, metadata, "", "") hook.initialize([], build_data) assert version_file.read_text() == helpers.dedent( @@ -219,16 +219,16 @@ def update(self, metadata): __version__ = '1.2.3' """ ) - assert build_data['artifacts'] == ['/baz.py'] + assert build_data["artifacts"] == ["/baz.py"] def test_custom(self, temp_dir, helpers): - config = {'path': 'baz.py', 'pattern': 'v = "(?P.+)"'} + config = {"path": "baz.py", "pattern": 'v = "(?P.+)"'} metadata = ProjectMetadata( str(temp_dir), PluginManager(), { - 'project': {'name': 'foo', 'dynamic': ['version']}, - 'tool': {'hatch': {'metadata': {'hooks': {'custom': {}}}}}, + "project": {"name": "foo", "dynamic": ["version"]}, + "tool": {"hatch": {"metadata": {"hooks": {"custom": {}}}}}, }, ) @@ -244,7 +244,7 @@ def update(self, metadata): """ ) ) - version_file = temp_dir / 'baz.py' + version_file = temp_dir / "baz.py" version_file.write_text( helpers.dedent( """ @@ -253,8 +253,8 @@ def update(self, metadata): ) ) - build_data = {'artifacts': []} - hook = VersionBuildHook(str(temp_dir), config, None, metadata, '', '') + build_data = {"artifacts": []} + hook = VersionBuildHook(str(temp_dir), config, None, metadata, "", "") hook.initialize([], build_data) assert version_file.read_text() == helpers.dedent( @@ -262,4 +262,4 @@ def update(self, metadata): v = "1.2.3" """ ) - assert build_data['artifacts'] == ['/baz.py'] + assert build_data["artifacts"] == ["/baz.py"] diff --git a/tests/backend/builders/plugin/test_interface.py b/tests/backend/builders/plugin/test_interface.py index afaa33de7..6af095444 100644 --- a/tests/backend/builders/plugin/test_interface.py +++ b/tests/backend/builders/plugin/test_interface.py @@ -41,158 +41,158 @@ def test_reuse(self, isolation): assert builder.raw_config is builder.raw_config is config def test_read(self, temp_dir): - project_file = temp_dir / 'pyproject.toml' - project_file.write_text('foo = 5') + project_file = temp_dir / "pyproject.toml" + project_file.write_text("foo = 5") with temp_dir.as_cwd(): builder = MockBuilder(str(temp_dir)) - assert builder.raw_config == builder.raw_config == {'foo': 5} + assert builder.raw_config == builder.raw_config == {"foo": 5} class TestMetadata: def test_base(self, isolation): - config = {'project': {'name': 'foo'}} + config = {"project": {"name": "foo"}} builder = MockBuilder(str(isolation), config=config) assert isinstance(builder.metadata, ProjectMetadata) - assert builder.metadata.core.name == 'foo' + assert builder.metadata.core.name == "foo" def test_core(self, isolation): - config = {'project': {}} + config = {"project": {}} builder = MockBuilder(str(isolation), config=config) - assert builder.project_config == builder.project_config == config['project'] + assert builder.project_config == builder.project_config == config["project"] def test_hatch(self, isolation): - config = {'tool': {'hatch': {}}} + config = {"tool": {"hatch": {}}} builder = MockBuilder(str(isolation), config=config) - assert builder.hatch_config is builder.hatch_config is config['tool']['hatch'] + assert builder.hatch_config is builder.hatch_config is config["tool"]["hatch"] def test_build_config(self, isolation): - config = {'tool': {'hatch': {'build': {}}}} + config = {"tool": {"hatch": {"build": {}}}} builder = MockBuilder(str(isolation), config=config) - assert builder.build_config is builder.build_config is config['tool']['hatch']['build'] + assert builder.build_config is builder.build_config is config["tool"]["hatch"]["build"] def test_build_config_not_table(self, isolation): - config = {'tool': {'hatch': {'build': 'foo'}}} + config = {"tool": {"hatch": {"build": "foo"}}} builder = MockBuilder(str(isolation), config=config) - with pytest.raises(TypeError, match='Field `tool.hatch.build` must be a table'): + with pytest.raises(TypeError, match="Field `tool.hatch.build` must be a table"): _ = builder.build_config def test_target_config(self, isolation): - config = {'tool': {'hatch': {'build': {'targets': {'foo': {}}}}}} + config = {"tool": {"hatch": {"build": {"targets": {"foo": {}}}}}} builder = MockBuilder(str(isolation), config=config) - builder.PLUGIN_NAME = 'foo' + builder.PLUGIN_NAME = "foo" - assert builder.target_config is builder.target_config is config['tool']['hatch']['build']['targets']['foo'] + assert builder.target_config is builder.target_config is config["tool"]["hatch"]["build"]["targets"]["foo"] def test_target_config_not_table(self, isolation): - config = {'tool': {'hatch': {'build': {'targets': {'foo': 'bar'}}}}} + config = {"tool": {"hatch": {"build": {"targets": {"foo": "bar"}}}}} builder = MockBuilder(str(isolation), config=config) - builder.PLUGIN_NAME = 'foo' + builder.PLUGIN_NAME = "foo" - with pytest.raises(TypeError, match='Field `tool.hatch.build.targets.foo` must be a table'): + with pytest.raises(TypeError, match="Field `tool.hatch.build.targets.foo` must be a table"): _ = builder.target_config class TestProjectID: def test_normalization(self, isolation): - config = {'project': {'name': 'my-app', 'version': '1.0.0-rc.1'}} + config = {"project": {"name": "my-app", "version": "1.0.0-rc.1"}} builder = MockBuilder(str(isolation), config=config) - assert builder.project_id == builder.project_id == 'my_app-1.0.0rc1' + assert builder.project_id == builder.project_id == "my_app-1.0.0rc1" class TestBuildValidation: def test_unknown_version(self, isolation): config = { - 'project': {'name': 'foo', 'version': '0.1.0'}, - 'tool': {'hatch': {'build': {'targets': {'foo': {'versions': ['1']}}}}}, + "project": {"name": "foo", "version": "0.1.0"}, + "tool": {"hatch": {"build": {"targets": {"foo": {"versions": ["1"]}}}}}, } builder = MockBuilder(str(isolation), config=config) - builder.PLUGIN_NAME = 'foo' - builder.get_version_api = lambda: {'1': str} + builder.PLUGIN_NAME = "foo" + builder.get_version_api = lambda: {"1": str} - with pytest.raises(ValueError, match='Unknown versions for target `foo`: 42, 9000'): - next(builder.build(directory=str(isolation), versions=['9000', '42'])) + with pytest.raises(ValueError, match="Unknown versions for target `foo`: 42, 9000"): + next(builder.build(directory=str(isolation), versions=["9000", "42"])) def test_invalid_metadata(self, isolation): config = { - 'project': {'name': 'foo', 'version': '0.1.0', 'dynamic': ['version']}, - 'tool': {'hatch': {'build': {'targets': {'foo': {'versions': ['1']}}}}}, + "project": {"name": "foo", "version": "0.1.0", "dynamic": ["version"]}, + "tool": {"hatch": {"build": {"targets": {"foo": {"versions": ["1"]}}}}}, } builder = MockBuilder(str(isolation), config=config) - builder.PLUGIN_NAME = 'foo' + builder.PLUGIN_NAME = "foo" with pytest.raises( ValueError, - match='Metadata field `version` cannot be both statically defined and listed in field `project.dynamic`', + match="Metadata field `version` cannot be both statically defined and listed in field `project.dynamic`", ): next(builder.build(directory=str(isolation))) class TestHookConfig: def test_unknown(self, isolation): - config = {'tool': {'hatch': {'build': {'hooks': {'foo': {'bar': 'baz'}}}}}} + config = {"tool": {"hatch": {"build": {"hooks": {"foo": {"bar": "baz"}}}}}} builder = MockBuilder(str(isolation), config=config) - with pytest.raises(ValueError, match='Unknown build hook: foo'): + with pytest.raises(ValueError, match="Unknown build hook: foo"): _ = builder.get_build_hooks(str(isolation)) class TestDirectoryRecursion: @pytest.mark.requires_unix def test_infinite_loop_prevention(self, temp_dir): - project_dir = temp_dir / 'project' + project_dir = temp_dir / "project" project_dir.ensure_dir_exists() with project_dir.as_cwd(): - config = {'tool': {'hatch': {'build': {'include': ['foo', 'README.md']}}}} + config = {"tool": {"hatch": {"build": {"include": ["foo", "README.md"]}}}} builder = MockBuilder(str(project_dir), config=config) - (project_dir / 'README.md').touch() - foo = project_dir / 'foo' + (project_dir / "README.md").touch() + foo = project_dir / "foo" foo.ensure_dir_exists() - (foo / 'bar.txt').touch() + (foo / "bar.txt").touch() - (foo / 'baz').symlink_to(project_dir) + (foo / "baz").symlink_to(project_dir) assert [f.path for f in builder.recurse_included_files()] == [ - str(project_dir / 'README.md'), - str(project_dir / 'foo' / 'bar.txt'), + str(project_dir / "README.md"), + str(project_dir / "foo" / "bar.txt"), ] def test_only_include(self, temp_dir): - project_dir = temp_dir / 'project' + project_dir = temp_dir / "project" project_dir.ensure_dir_exists() with project_dir.as_cwd(): - config = {'tool': {'hatch': {'build': {'only-include': ['foo'], 'artifacts': ['README.md']}}}} + config = {"tool": {"hatch": {"build": {"only-include": ["foo"], "artifacts": ["README.md"]}}}} builder = MockBuilder(str(project_dir), config=config) - (project_dir / 'README.md').touch() - foo = project_dir / 'foo' + (project_dir / "README.md").touch() + foo = project_dir / "foo" foo.ensure_dir_exists() - (foo / 'bar.txt').touch() + (foo / "bar.txt").touch() - assert [f.path for f in builder.recurse_included_files()] == [str(project_dir / 'foo' / 'bar.txt')] + assert [f.path for f in builder.recurse_included_files()] == [str(project_dir / "foo" / "bar.txt")] def test_no_duplication_force_include_only(self, temp_dir): - project_dir = temp_dir / 'project' + project_dir = temp_dir / "project" project_dir.ensure_dir_exists() with project_dir.as_cwd(): config = { - 'tool': { - 'hatch': { - 'build': { - 'force-include': { - '../external.txt': 'new/target2.txt', - 'old': 'new', + "tool": { + "hatch": { + "build": { + "force-include": { + "../external.txt": "new/target2.txt", + "old": "new", }, } } @@ -200,105 +200,105 @@ def test_no_duplication_force_include_only(self, temp_dir): } builder = MockBuilder(str(project_dir), config=config) - (project_dir / 'foo.txt').touch() - old = project_dir / 'old' + (project_dir / "foo.txt").touch() + old = project_dir / "old" old.ensure_dir_exists() - (old / 'target1.txt').touch() - (old / 'target2.txt').touch() + (old / "target1.txt").touch() + (old / "target2.txt").touch() - (temp_dir / 'external.txt').touch() + (temp_dir / "external.txt").touch() build_data = builder.get_default_build_data() builder.set_build_data_defaults(build_data) with builder.config.set_build_data(build_data): assert [(f.path, f.distribution_path) for f in builder.recurse_included_files()] == [ - (str(project_dir / 'foo.txt'), 'foo.txt'), - (str(project_dir / 'old' / 'target1.txt'), f'new{path_sep}target1.txt'), - (str(temp_dir / 'external.txt'), f'new{path_sep}target2.txt'), + (str(project_dir / "foo.txt"), "foo.txt"), + (str(project_dir / "old" / "target1.txt"), f"new{path_sep}target1.txt"), + (str(temp_dir / "external.txt"), f"new{path_sep}target2.txt"), ] def test_no_duplication_force_include_and_selection(self, temp_dir): - project_dir = temp_dir / 'project' + project_dir = temp_dir / "project" project_dir.ensure_dir_exists() with project_dir.as_cwd(): config = { - 'tool': { - 'hatch': { - 'build': { - 'include': ['foo.txt', 'bar.txt', 'baz.txt'], - 'force-include': {'../external.txt': 'new/file.txt'}, + "tool": { + "hatch": { + "build": { + "include": ["foo.txt", "bar.txt", "baz.txt"], + "force-include": {"../external.txt": "new/file.txt"}, } } } } builder = MockBuilder(str(project_dir), config=config) - (project_dir / 'foo.txt').touch() - (project_dir / 'bar.txt').touch() - (project_dir / 'baz.txt').touch() - (temp_dir / 'external.txt').touch() + (project_dir / "foo.txt").touch() + (project_dir / "bar.txt").touch() + (project_dir / "baz.txt").touch() + (temp_dir / "external.txt").touch() build_data = builder.get_default_build_data() builder.set_build_data_defaults(build_data) - build_data['force_include']['bar.txt'] = 'bar.txt' + build_data["force_include"]["bar.txt"] = "bar.txt" with builder.config.set_build_data(build_data): assert [(f.path, f.distribution_path) for f in builder.recurse_included_files()] == [ - (str(project_dir / 'baz.txt'), 'baz.txt'), - (str(project_dir / 'foo.txt'), 'foo.txt'), - (str(temp_dir / 'external.txt'), f'new{path_sep}file.txt'), - (str(project_dir / 'bar.txt'), 'bar.txt'), + (str(project_dir / "baz.txt"), "baz.txt"), + (str(project_dir / "foo.txt"), "foo.txt"), + (str(temp_dir / "external.txt"), f"new{path_sep}file.txt"), + (str(project_dir / "bar.txt"), "bar.txt"), ] def test_no_duplication_force_include_with_sources(self, temp_dir): - project_dir = temp_dir / 'project' + project_dir = temp_dir / "project" project_dir.ensure_dir_exists() with project_dir.as_cwd(): config = { - 'tool': { - 'hatch': { - 'build': { - 'include': ['src'], - 'sources': ['src'], - 'force-include': {'../external.txt': 'new/file.txt'}, + "tool": { + "hatch": { + "build": { + "include": ["src"], + "sources": ["src"], + "force-include": {"../external.txt": "new/file.txt"}, } } } } builder = MockBuilder(str(project_dir), config=config) - src_dir = project_dir / 'src' + src_dir = project_dir / "src" src_dir.mkdir() - (src_dir / 'foo.txt').touch() - (src_dir / 'bar.txt').touch() - (src_dir / 'baz.txt').touch() - (temp_dir / 'external.txt').touch() + (src_dir / "foo.txt").touch() + (src_dir / "bar.txt").touch() + (src_dir / "baz.txt").touch() + (temp_dir / "external.txt").touch() build_data = builder.get_default_build_data() builder.set_build_data_defaults(build_data) - build_data['force_include']['src/bar.txt'] = 'bar.txt' + build_data["force_include"]["src/bar.txt"] = "bar.txt" with builder.config.set_build_data(build_data): assert [(f.path, f.distribution_path) for f in builder.recurse_included_files()] == [ - (str(src_dir / 'baz.txt'), 'baz.txt'), - (str(src_dir / 'foo.txt'), 'foo.txt'), - (str(temp_dir / 'external.txt'), f'new{path_sep}file.txt'), - (str(src_dir / 'bar.txt'), 'bar.txt'), + (str(src_dir / "baz.txt"), "baz.txt"), + (str(src_dir / "foo.txt"), "foo.txt"), + (str(temp_dir / "external.txt"), f"new{path_sep}file.txt"), + (str(src_dir / "bar.txt"), "bar.txt"), ] def test_exists(self, temp_dir): - project_dir = temp_dir / 'project' + project_dir = temp_dir / "project" project_dir.ensure_dir_exists() with project_dir.as_cwd(): config = { - 'tool': { - 'hatch': { - 'build': { - 'force-include': { - '../notfound': 'target.txt', + "tool": { + "hatch": { + "build": { + "force-include": { + "../notfound": "target.txt", }, } } @@ -307,27 +307,28 @@ def test_exists(self, temp_dir): builder = MockBuilder(str(project_dir), config=config) build_data = builder.get_default_build_data() builder.set_build_data_defaults(build_data) - with builder.config.set_build_data(build_data), pytest.raises( - FileNotFoundError, match='Forced include not found' + with ( + builder.config.set_build_data(build_data), + pytest.raises(FileNotFoundError, match="Forced include not found"), ): list(builder.recurse_included_files()) def test_order(self, temp_dir): - project_dir = temp_dir / 'project' + project_dir = temp_dir / "project" project_dir.ensure_dir_exists() with project_dir.as_cwd(): config = { - 'tool': { - 'hatch': { - 'build': { - 'sources': ['src'], - 'include': ['src/foo', 'bar', 'README.md', 'tox.ini'], - 'exclude': ['**/foo/baz.txt'], - 'force-include': { - '../external1.txt': 'nested/target2.txt', - '../external2.txt': 'nested/target1.txt', - '../external': 'nested', + "tool": { + "hatch": { + "build": { + "sources": ["src"], + "include": ["src/foo", "bar", "README.md", "tox.ini"], + "exclude": ["**/foo/baz.txt"], + "force-include": { + "../external1.txt": "nested/target2.txt", + "../external2.txt": "nested/target1.txt", + "../external": "nested", }, } } @@ -335,54 +336,54 @@ def test_order(self, temp_dir): } builder = MockBuilder(str(project_dir), config=config) - foo = project_dir / 'src' / 'foo' + foo = project_dir / "src" / "foo" foo.ensure_dir_exists() - (foo / 'bar.txt').touch() - (foo / 'baz.txt').touch() + (foo / "bar.txt").touch() + (foo / "baz.txt").touch() - bar = project_dir / 'bar' + bar = project_dir / "bar" bar.ensure_dir_exists() - (bar / 'foo.txt').touch() + (bar / "foo.txt").touch() # Excluded for name in EXCLUDED_DIRECTORIES: excluded_dir = bar / name excluded_dir.ensure_dir_exists() - (excluded_dir / 'file.ext').touch() + (excluded_dir / "file.ext").touch() for name in EXCLUDED_FILES: excluded_file = bar / name excluded_file.touch() - (project_dir / 'README.md').touch() - (project_dir / 'tox.ini').touch() + (project_dir / "README.md").touch() + (project_dir / "tox.ini").touch() - (temp_dir / 'external1.txt').touch() - (temp_dir / 'external2.txt').touch() + (temp_dir / "external1.txt").touch() + (temp_dir / "external2.txt").touch() - external = temp_dir / 'external' + external = temp_dir / "external" external.ensure_dir_exists() - (external / 'external1.txt').touch() - (external / 'external2.txt').touch() + (external / "external1.txt").touch() + (external / "external2.txt").touch() # Excluded for name in EXCLUDED_DIRECTORIES: excluded_dir = external / name excluded_dir.ensure_dir_exists() - (excluded_dir / 'file.ext').touch() + (excluded_dir / "file.ext").touch() for name in EXCLUDED_FILES: excluded_file = external / name excluded_file.touch() assert [(f.path, f.distribution_path) for f in builder.recurse_included_files()] == [ - (str(project_dir / 'README.md'), 'README.md'), - (str(project_dir / 'tox.ini'), 'tox.ini'), + (str(project_dir / "README.md"), "README.md"), + (str(project_dir / "tox.ini"), "tox.ini"), ( - str(project_dir / 'bar' / 'foo.txt'), - f'bar{path_sep}foo.txt', + str(project_dir / "bar" / "foo.txt"), + f"bar{path_sep}foo.txt", ), - (str(project_dir / 'src' / 'foo' / 'bar.txt'), f'foo{path_sep}bar.txt'), - (str(temp_dir / 'external' / 'external1.txt'), f'nested{path_sep}external1.txt'), - (str(temp_dir / 'external' / 'external2.txt'), f'nested{path_sep}external2.txt'), - (str(temp_dir / 'external2.txt'), f'nested{path_sep}target1.txt'), - (str(temp_dir / 'external1.txt'), f'nested{path_sep}target2.txt'), + (str(project_dir / "src" / "foo" / "bar.txt"), f"foo{path_sep}bar.txt"), + (str(temp_dir / "external" / "external1.txt"), f"nested{path_sep}external1.txt"), + (str(temp_dir / "external" / "external2.txt"), f"nested{path_sep}external2.txt"), + (str(temp_dir / "external2.txt"), f"nested{path_sep}target1.txt"), + (str(temp_dir / "external1.txt"), f"nested{path_sep}target2.txt"), ] diff --git a/tests/backend/builders/test_binary.py b/tests/backend/builders/test_binary.py index 410146a11..5cda740f8 100644 --- a/tests/backend/builders/test_binary.py +++ b/tests/backend/builders/test_binary.py @@ -28,24 +28,24 @@ def __hash__(self): # no cov def cargo_install(*args: Any, **_kwargs: Any) -> subprocess.CompletedProcess: - executable_name = 'pyapp.exe' if sys.platform == 'win32' else 'pyapp' + executable_name = "pyapp.exe" if sys.platform == "win32" else "pyapp" install_command: list[str] = args[0] - repo_path = os.environ.get('PYAPP_REPO', '') + repo_path = os.environ.get("PYAPP_REPO", "") if repo_path: - temp_dir = install_command[install_command.index('--target-dir') + 1] + temp_dir = install_command[install_command.index("--target-dir") + 1] - build_target = os.environ.get('CARGO_BUILD_TARGET', '') + build_target = os.environ.get("CARGO_BUILD_TARGET", "") if build_target: - executable = Path(temp_dir, build_target, 'release', executable_name) + executable = Path(temp_dir, build_target, "release", executable_name) else: - executable = Path(temp_dir, 'release', executable_name) + executable = Path(temp_dir, "release", executable_name) executable.parent.ensure_dir_exists() executable.touch() else: - temp_dir = install_command[install_command.index('--root') + 1] + temp_dir = install_command[install_command.index("--root") + 1] - executable = Path(temp_dir, 'bin', executable_name) + executable = Path(temp_dir, "bin", executable_name) executable.parent.ensure_dir_exists() executable.touch() @@ -63,217 +63,217 @@ def test_class_legacy(): def test_default_versions(isolation): builder = BinaryBuilder(str(isolation)) - assert builder.get_default_versions() == ['bootstrap'] + assert builder.get_default_versions() == ["bootstrap"] class TestScripts: def test_unset(self, isolation): - config = {'project': {'name': 'My.App', 'version': '0.1.0'}} + config = {"project": {"name": "My.App", "version": "0.1.0"}} builder = BinaryBuilder(str(isolation), config=config) assert builder.config.scripts == builder.config.scripts == [] def test_default(self, isolation): config = { - 'project': { - 'name': 'My.App', - 'version': '0.1.0', - 'scripts': {'b': 'foo.bar.baz:cli', 'a': 'baz.bar.foo:cli'}, + "project": { + "name": "My.App", + "version": "0.1.0", + "scripts": {"b": "foo.bar.baz:cli", "a": "baz.bar.foo:cli"}, } } builder = BinaryBuilder(str(isolation), config=config) - assert builder.config.scripts == ['a', 'b'] + assert builder.config.scripts == ["a", "b"] def test_specific(self, isolation): config = { - 'project': { - 'name': 'My.App', - 'version': '0.1.0', - 'scripts': {'b': 'foo.bar.baz:cli', 'a': 'baz.bar.foo:cli'}, + "project": { + "name": "My.App", + "version": "0.1.0", + "scripts": {"b": "foo.bar.baz:cli", "a": "baz.bar.foo:cli"}, }, - 'tool': {'hatch': {'build': {'targets': {'binary': {'scripts': ['a', 'a']}}}}}, + "tool": {"hatch": {"build": {"targets": {"binary": {"scripts": ["a", "a"]}}}}}, } builder = BinaryBuilder(str(isolation), config=config) - assert builder.config.scripts == ['a'] + assert builder.config.scripts == ["a"] def test_not_array(self, isolation): config = { - 'project': { - 'name': 'My.App', - 'version': '0.1.0', - 'scripts': {'b': 'foo.bar.baz:cli', 'a': 'baz.bar.foo:cli'}, + "project": { + "name": "My.App", + "version": "0.1.0", + "scripts": {"b": "foo.bar.baz:cli", "a": "baz.bar.foo:cli"}, }, - 'tool': {'hatch': {'build': {'targets': {'binary': {'scripts': 9000}}}}}, + "tool": {"hatch": {"build": {"targets": {"binary": {"scripts": 9000}}}}}, } builder = BinaryBuilder(str(isolation), config=config) - with pytest.raises(TypeError, match='Field `tool.hatch.build.targets.binary.scripts` must be an array'): + with pytest.raises(TypeError, match="Field `tool.hatch.build.targets.binary.scripts` must be an array"): _ = builder.config.scripts def test_script_not_string(self, isolation): config = { - 'project': { - 'name': 'My.App', - 'version': '0.1.0', - 'scripts': {'b': 'foo.bar.baz:cli', 'a': 'baz.bar.foo:cli'}, + "project": { + "name": "My.App", + "version": "0.1.0", + "scripts": {"b": "foo.bar.baz:cli", "a": "baz.bar.foo:cli"}, }, - 'tool': {'hatch': {'build': {'targets': {'binary': {'scripts': [9000]}}}}}, + "tool": {"hatch": {"build": {"targets": {"binary": {"scripts": [9000]}}}}}, } builder = BinaryBuilder(str(isolation), config=config) with pytest.raises( - TypeError, match='Script #1 of field `tool.hatch.build.targets.binary.scripts` must be a string' + TypeError, match="Script #1 of field `tool.hatch.build.targets.binary.scripts` must be a string" ): _ = builder.config.scripts def test_unknown_script(self, isolation): config = { - 'project': { - 'name': 'My.App', - 'version': '0.1.0', - 'scripts': {'b': 'foo.bar.baz:cli', 'a': 'baz.bar.foo:cli'}, + "project": { + "name": "My.App", + "version": "0.1.0", + "scripts": {"b": "foo.bar.baz:cli", "a": "baz.bar.foo:cli"}, }, - 'tool': {'hatch': {'build': {'targets': {'binary': {'scripts': ['c']}}}}}, + "tool": {"hatch": {"build": {"targets": {"binary": {"scripts": ["c"]}}}}}, } builder = BinaryBuilder(str(isolation), config=config) - with pytest.raises(ValueError, match='Unknown script in field `tool.hatch.build.targets.binary.scripts`: c'): + with pytest.raises(ValueError, match="Unknown script in field `tool.hatch.build.targets.binary.scripts`: c"): _ = builder.config.scripts class TestPythonVersion: def test_default_no_source(self, isolation): - config = {'project': {'name': 'My.App', 'version': '0.1.0'}} + config = {"project": {"name": "My.App", "version": "0.1.0"}} builder = BinaryBuilder(str(isolation), config=config) assert builder.config.python_version == builder.config.python_version == builder.config.SUPPORTED_VERSIONS[0] def test_default_explicit_source(self, isolation): - config = {'project': {'name': 'My.App', 'version': '0.1.0'}} + config = {"project": {"name": "My.App", "version": "0.1.0"}} builder = BinaryBuilder(str(isolation), config=config) - with EnvVars({'PYAPP_DISTRIBUTION_SOURCE': 'url'}): - assert builder.config.python_version == builder.config.python_version == '' + with EnvVars({"PYAPP_DISTRIBUTION_SOURCE": "url"}): + assert builder.config.python_version == builder.config.python_version == "" def test_set(self, isolation): config = { - 'project': { - 'name': 'My.App', - 'version': '0.1.0', + "project": { + "name": "My.App", + "version": "0.1.0", }, - 'tool': {'hatch': {'build': {'targets': {'binary': {'python-version': '4.0'}}}}}, + "tool": {"hatch": {"build": {"targets": {"binary": {"python-version": "4.0"}}}}}, } builder = BinaryBuilder(str(isolation), config=config) - assert builder.config.python_version == '4.0' + assert builder.config.python_version == "4.0" def test_not_string(self, isolation): config = { - 'project': { - 'name': 'My.App', - 'version': '0.1.0', + "project": { + "name": "My.App", + "version": "0.1.0", }, - 'tool': {'hatch': {'build': {'targets': {'binary': {'python-version': 9000}}}}}, + "tool": {"hatch": {"build": {"targets": {"binary": {"python-version": 9000}}}}}, } builder = BinaryBuilder(str(isolation), config=config) - with pytest.raises(TypeError, match='Field `tool.hatch.build.targets.binary.python-version` must be a string'): + with pytest.raises(TypeError, match="Field `tool.hatch.build.targets.binary.python-version` must be a string"): _ = builder.config.python_version def test_compatibility(self, isolation): config = { - 'project': { - 'name': 'My.App', - 'version': '0.1.0', - 'requires-python': '<3.11', + "project": { + "name": "My.App", + "version": "0.1.0", + "requires-python": "<3.11", }, } builder = BinaryBuilder(str(isolation), config=config) - assert builder.config.python_version == '3.10' + assert builder.config.python_version == "3.10" def test_incompatible(self, isolation): config = { - 'project': { - 'name': 'My.App', - 'version': '0.1.0', - 'requires-python': '>9000', + "project": { + "name": "My.App", + "version": "0.1.0", + "requires-python": ">9000", }, } builder = BinaryBuilder(str(isolation), config=config) with pytest.raises( - ValueError, match='Field `project.requires-python` is incompatible with the known distributions' + ValueError, match="Field `project.requires-python` is incompatible with the known distributions" ): _ = builder.config.python_version class TestPyAppVersion: def test_default(self, isolation): - config = {'project': {'name': 'My.App', 'version': '0.1.0'}} + config = {"project": {"name": "My.App", "version": "0.1.0"}} builder = BinaryBuilder(str(isolation), config=config) - assert builder.config.pyapp_version == builder.config.pyapp_version == '' + assert builder.config.pyapp_version == builder.config.pyapp_version == "" def test_set(self, isolation): config = { - 'project': { - 'name': 'My.App', - 'version': '0.1.0', + "project": { + "name": "My.App", + "version": "0.1.0", }, - 'tool': {'hatch': {'build': {'targets': {'binary': {'pyapp-version': '9000'}}}}}, + "tool": {"hatch": {"build": {"targets": {"binary": {"pyapp-version": "9000"}}}}}, } builder = BinaryBuilder(str(isolation), config=config) - assert builder.config.pyapp_version == '9000' + assert builder.config.pyapp_version == "9000" def test_not_string(self, isolation): config = { - 'project': { - 'name': 'My.App', - 'version': '0.1.0', + "project": { + "name": "My.App", + "version": "0.1.0", }, - 'tool': {'hatch': {'build': {'targets': {'binary': {'pyapp-version': 9000}}}}}, + "tool": {"hatch": {"build": {"targets": {"binary": {"pyapp-version": 9000}}}}}, } builder = BinaryBuilder(str(isolation), config=config) - with pytest.raises(TypeError, match='Field `tool.hatch.build.targets.binary.pyapp-version` must be a string'): + with pytest.raises(TypeError, match="Field `tool.hatch.build.targets.binary.pyapp-version` must be a string"): _ = builder.config.pyapp_version class TestBuildBootstrap: def test_default(self, hatch, temp_dir, mocker): - subprocess_run = mocker.patch('subprocess.run', side_effect=cargo_install) + subprocess_run = mocker.patch("subprocess.run", side_effect=cargo_install) - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' + project_path = temp_dir / "my-app" config = { - 'project': {'name': project_name, 'version': '0.1.0'}, - 'tool': { - 'hatch': { - 'build': {'targets': {'binary': {'versions': ['bootstrap']}}}, + "project": {"name": project_name, "version": "0.1.0"}, + "tool": { + "hatch": { + "build": {"targets": {"binary": {"versions": ["bootstrap"]}}}, }, }, } builder = BinaryBuilder(str(project_path), config=config) - build_path = project_path / 'dist' + build_path = project_path / "dist" with project_path.as_cwd(): artifacts = list(builder.build()) subprocess_run.assert_called_once_with( - ['cargo', 'install', 'pyapp', '--force', '--root', mocker.ANY], + ["cargo", "install", "pyapp", "--force", "--root", mocker.ANY], cwd=mocker.ANY, - env=ExpectedEnvVars({'PYAPP_PROJECT_NAME': 'my-app', 'PYAPP_PROJECT_VERSION': '0.1.0'}), + env=ExpectedEnvVars({"PYAPP_PROJECT_NAME": "my-app", "PYAPP_PROJECT_VERSION": "0.1.0"}), ) assert len(artifacts) == 1 @@ -282,38 +282,38 @@ def test_default(self, hatch, temp_dir, mocker): build_artifacts = list(build_path.iterdir()) assert len(build_artifacts) == 1 assert expected_artifact == str(build_artifacts[0]) - assert (build_path / 'binary' / ('my-app-0.1.0.exe' if sys.platform == 'win32' else 'my-app-0.1.0')).is_file() + assert (build_path / "binary" / ("my-app-0.1.0.exe" if sys.platform == "win32" else "my-app-0.1.0")).is_file() def test_default_build_target(self, hatch, temp_dir, mocker): - subprocess_run = mocker.patch('subprocess.run', side_effect=cargo_install) + subprocess_run = mocker.patch("subprocess.run", side_effect=cargo_install) - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' + project_path = temp_dir / "my-app" config = { - 'project': {'name': project_name, 'version': '0.1.0'}, - 'tool': { - 'hatch': { - 'build': {'targets': {'binary': {'versions': ['bootstrap']}}}, + "project": {"name": project_name, "version": "0.1.0"}, + "tool": { + "hatch": { + "build": {"targets": {"binary": {"versions": ["bootstrap"]}}}, }, }, } builder = BinaryBuilder(str(project_path), config=config) - build_path = project_path / 'dist' + build_path = project_path / "dist" - with project_path.as_cwd({'CARGO_BUILD_TARGET': 'target'}): + with project_path.as_cwd({"CARGO_BUILD_TARGET": "target"}): artifacts = list(builder.build()) subprocess_run.assert_called_once_with( - ['cargo', 'install', 'pyapp', '--force', '--root', mocker.ANY], + ["cargo", "install", "pyapp", "--force", "--root", mocker.ANY], cwd=mocker.ANY, - env=ExpectedEnvVars({'PYAPP_PROJECT_NAME': 'my-app', 'PYAPP_PROJECT_VERSION': '0.1.0'}), + env=ExpectedEnvVars({"PYAPP_PROJECT_NAME": "my-app", "PYAPP_PROJECT_VERSION": "0.1.0"}), ) assert len(artifacts) == 1 @@ -323,42 +323,42 @@ def test_default_build_target(self, hatch, temp_dir, mocker): assert len(build_artifacts) == 1 assert expected_artifact == str(build_artifacts[0]) assert ( - build_path / 'binary' / ('my-app-0.1.0-target.exe' if sys.platform == 'win32' else 'my-app-0.1.0-target') + build_path / "binary" / ("my-app-0.1.0-target.exe" if sys.platform == "win32" else "my-app-0.1.0-target") ).is_file() def test_scripts(self, hatch, temp_dir, mocker): - subprocess_run = mocker.patch('subprocess.run', side_effect=cargo_install) + subprocess_run = mocker.patch("subprocess.run", side_effect=cargo_install) - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' + project_path = temp_dir / "my-app" config = { - 'project': {'name': project_name, 'version': '0.1.0', 'scripts': {'foo': 'bar.baz:cli'}}, - 'tool': { - 'hatch': { - 'build': {'targets': {'binary': {'versions': ['bootstrap']}}}, + "project": {"name": project_name, "version": "0.1.0", "scripts": {"foo": "bar.baz:cli"}}, + "tool": { + "hatch": { + "build": {"targets": {"binary": {"versions": ["bootstrap"]}}}, }, }, } builder = BinaryBuilder(str(project_path), config=config) - build_path = project_path / 'dist' + build_path = project_path / "dist" with project_path.as_cwd(): artifacts = list(builder.build()) subprocess_run.assert_called_once_with( - ['cargo', 'install', 'pyapp', '--force', '--root', mocker.ANY], + ["cargo", "install", "pyapp", "--force", "--root", mocker.ANY], cwd=mocker.ANY, env=ExpectedEnvVars({ - 'PYAPP_PROJECT_NAME': 'my-app', - 'PYAPP_PROJECT_VERSION': '0.1.0', - 'PYAPP_EXEC_SPEC': 'bar.baz:cli', + "PYAPP_PROJECT_NAME": "my-app", + "PYAPP_PROJECT_VERSION": "0.1.0", + "PYAPP_EXEC_SPEC": "bar.baz:cli", }), ) @@ -368,41 +368,41 @@ def test_scripts(self, hatch, temp_dir, mocker): build_artifacts = list(build_path.iterdir()) assert len(build_artifacts) == 1 assert expected_artifact == str(build_artifacts[0]) - assert (build_path / 'binary' / ('foo-0.1.0.exe' if sys.platform == 'win32' else 'foo-0.1.0')).is_file() + assert (build_path / "binary" / ("foo-0.1.0.exe" if sys.platform == "win32" else "foo-0.1.0")).is_file() def test_scripts_build_target(self, hatch, temp_dir, mocker): - subprocess_run = mocker.patch('subprocess.run', side_effect=cargo_install) + subprocess_run = mocker.patch("subprocess.run", side_effect=cargo_install) - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' + project_path = temp_dir / "my-app" config = { - 'project': {'name': project_name, 'version': '0.1.0', 'scripts': {'foo': 'bar.baz:cli'}}, - 'tool': { - 'hatch': { - 'build': {'targets': {'binary': {'versions': ['bootstrap']}}}, + "project": {"name": project_name, "version": "0.1.0", "scripts": {"foo": "bar.baz:cli"}}, + "tool": { + "hatch": { + "build": {"targets": {"binary": {"versions": ["bootstrap"]}}}, }, }, } builder = BinaryBuilder(str(project_path), config=config) - build_path = project_path / 'dist' + build_path = project_path / "dist" - with project_path.as_cwd({'CARGO_BUILD_TARGET': 'target'}): + with project_path.as_cwd({"CARGO_BUILD_TARGET": "target"}): artifacts = list(builder.build()) subprocess_run.assert_called_once_with( - ['cargo', 'install', 'pyapp', '--force', '--root', mocker.ANY], + ["cargo", "install", "pyapp", "--force", "--root", mocker.ANY], cwd=mocker.ANY, env=ExpectedEnvVars({ - 'PYAPP_PROJECT_NAME': 'my-app', - 'PYAPP_PROJECT_VERSION': '0.1.0', - 'PYAPP_EXEC_SPEC': 'bar.baz:cli', + "PYAPP_PROJECT_NAME": "my-app", + "PYAPP_PROJECT_VERSION": "0.1.0", + "PYAPP_EXEC_SPEC": "bar.baz:cli", }), ) @@ -413,39 +413,39 @@ def test_scripts_build_target(self, hatch, temp_dir, mocker): assert len(build_artifacts) == 1 assert expected_artifact == str(build_artifacts[0]) assert ( - build_path / 'binary' / ('foo-0.1.0-target.exe' if sys.platform == 'win32' else 'foo-0.1.0-target') + build_path / "binary" / ("foo-0.1.0-target.exe" if sys.platform == "win32" else "foo-0.1.0-target") ).is_file() def test_custom_cargo(self, hatch, temp_dir, mocker): - subprocess_run = mocker.patch('subprocess.run', side_effect=cargo_install) + subprocess_run = mocker.patch("subprocess.run", side_effect=cargo_install) - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' + project_path = temp_dir / "my-app" config = { - 'project': {'name': project_name, 'version': '0.1.0'}, - 'tool': { - 'hatch': { - 'build': {'targets': {'binary': {'versions': ['bootstrap']}}}, + "project": {"name": project_name, "version": "0.1.0"}, + "tool": { + "hatch": { + "build": {"targets": {"binary": {"versions": ["bootstrap"]}}}, }, }, } builder = BinaryBuilder(str(project_path), config=config) - build_path = project_path / 'dist' + build_path = project_path / "dist" - with project_path.as_cwd({'CARGO': 'cross'}): + with project_path.as_cwd({"CARGO": "cross"}): artifacts = list(builder.build()) subprocess_run.assert_called_once_with( - ['cross', 'install', 'pyapp', '--force', '--root', mocker.ANY], + ["cross", "install", "pyapp", "--force", "--root", mocker.ANY], cwd=mocker.ANY, - env=ExpectedEnvVars({'PYAPP_PROJECT_NAME': 'my-app', 'PYAPP_PROJECT_VERSION': '0.1.0'}), + env=ExpectedEnvVars({"PYAPP_PROJECT_NAME": "my-app", "PYAPP_PROJECT_VERSION": "0.1.0"}), ) assert len(artifacts) == 1 @@ -454,65 +454,65 @@ def test_custom_cargo(self, hatch, temp_dir, mocker): build_artifacts = list(build_path.iterdir()) assert len(build_artifacts) == 1 assert expected_artifact == str(build_artifacts[0]) - assert (build_path / 'binary' / ('my-app-0.1.0.exe' if sys.platform == 'win32' else 'my-app-0.1.0')).is_file() + assert (build_path / "binary" / ("my-app-0.1.0.exe" if sys.platform == "win32" else "my-app-0.1.0")).is_file() def test_no_cargo(self, hatch, temp_dir, mocker): - mocker.patch('shutil.which', return_value=None) + mocker.patch("shutil.which", return_value=None) - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' + project_path = temp_dir / "my-app" config = { - 'project': {'name': project_name, 'version': '0.1.0'}, - 'tool': { - 'hatch': { - 'build': {'targets': {'binary': {'versions': ['bootstrap']}}}, + "project": {"name": project_name, "version": "0.1.0"}, + "tool": { + "hatch": { + "build": {"targets": {"binary": {"versions": ["bootstrap"]}}}, }, }, } builder = BinaryBuilder(str(project_path), config=config) - with pytest.raises(OSError, match='Executable `cargo` could not be found on PATH'), project_path.as_cwd(): + with pytest.raises(OSError, match="Executable `cargo` could not be found on PATH"), project_path.as_cwd(): next(builder.build()) def test_python_version(self, hatch, temp_dir, mocker): - subprocess_run = mocker.patch('subprocess.run', side_effect=cargo_install) + subprocess_run = mocker.patch("subprocess.run", side_effect=cargo_install) - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' + project_path = temp_dir / "my-app" config = { - 'project': {'name': project_name, 'version': '0.1.0'}, - 'tool': { - 'hatch': { - 'build': {'targets': {'binary': {'versions': ['bootstrap'], 'python-version': '4.0'}}}, + "project": {"name": project_name, "version": "0.1.0"}, + "tool": { + "hatch": { + "build": {"targets": {"binary": {"versions": ["bootstrap"], "python-version": "4.0"}}}, }, }, } builder = BinaryBuilder(str(project_path), config=config) - build_path = project_path / 'dist' + build_path = project_path / "dist" with project_path.as_cwd(): artifacts = list(builder.build()) subprocess_run.assert_called_once_with( - ['cargo', 'install', 'pyapp', '--force', '--root', mocker.ANY], + ["cargo", "install", "pyapp", "--force", "--root", mocker.ANY], cwd=mocker.ANY, env=ExpectedEnvVars({ - 'PYAPP_PROJECT_NAME': 'my-app', - 'PYAPP_PROJECT_VERSION': '0.1.0', - 'PYAPP_PYTHON_VERSION': '4.0', + "PYAPP_PROJECT_NAME": "my-app", + "PYAPP_PROJECT_VERSION": "0.1.0", + "PYAPP_PYTHON_VERSION": "4.0", }), ) @@ -522,38 +522,38 @@ def test_python_version(self, hatch, temp_dir, mocker): build_artifacts = list(build_path.iterdir()) assert len(build_artifacts) == 1 assert expected_artifact == str(build_artifacts[0]) - assert (build_path / 'binary' / ('my-app-0.1.0.exe' if sys.platform == 'win32' else 'my-app-0.1.0')).is_file() + assert (build_path / "binary" / ("my-app-0.1.0.exe" if sys.platform == "win32" else "my-app-0.1.0")).is_file() def test_pyapp_version(self, hatch, temp_dir, mocker): - subprocess_run = mocker.patch('subprocess.run', side_effect=cargo_install) + subprocess_run = mocker.patch("subprocess.run", side_effect=cargo_install) - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' + project_path = temp_dir / "my-app" config = { - 'project': {'name': project_name, 'version': '0.1.0'}, - 'tool': { - 'hatch': { - 'build': {'targets': {'binary': {'versions': ['bootstrap'], 'pyapp-version': '9000'}}}, + "project": {"name": project_name, "version": "0.1.0"}, + "tool": { + "hatch": { + "build": {"targets": {"binary": {"versions": ["bootstrap"], "pyapp-version": "9000"}}}, }, }, } builder = BinaryBuilder(str(project_path), config=config) - build_path = project_path / 'dist' + build_path = project_path / "dist" with project_path.as_cwd(): artifacts = list(builder.build()) subprocess_run.assert_called_once_with( - ['cargo', 'install', 'pyapp', '--force', '--root', mocker.ANY, '--version', '9000'], + ["cargo", "install", "pyapp", "--force", "--root", mocker.ANY, "--version", "9000"], cwd=mocker.ANY, - env=ExpectedEnvVars({'PYAPP_PROJECT_NAME': 'my-app', 'PYAPP_PROJECT_VERSION': '0.1.0'}), + env=ExpectedEnvVars({"PYAPP_PROJECT_NAME": "my-app", "PYAPP_PROJECT_VERSION": "0.1.0"}), ) assert len(artifacts) == 1 @@ -562,38 +562,38 @@ def test_pyapp_version(self, hatch, temp_dir, mocker): build_artifacts = list(build_path.iterdir()) assert len(build_artifacts) == 1 assert expected_artifact == str(build_artifacts[0]) - assert (build_path / 'binary' / ('my-app-0.1.0.exe' if sys.platform == 'win32' else 'my-app-0.1.0')).is_file() + assert (build_path / "binary" / ("my-app-0.1.0.exe" if sys.platform == "win32" else "my-app-0.1.0")).is_file() def test_verbosity(self, hatch, temp_dir, mocker): - subprocess_run = mocker.patch('subprocess.run', side_effect=cargo_install) + subprocess_run = mocker.patch("subprocess.run", side_effect=cargo_install) - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' + project_path = temp_dir / "my-app" config = { - 'project': {'name': project_name, 'version': '0.1.0'}, - 'tool': { - 'hatch': { - 'build': {'targets': {'binary': {'versions': ['bootstrap']}}}, + "project": {"name": project_name, "version": "0.1.0"}, + "tool": { + "hatch": { + "build": {"targets": {"binary": {"versions": ["bootstrap"]}}}, }, }, } builder = BinaryBuilder(str(project_path), config=config) - build_path = project_path / 'dist' + build_path = project_path / "dist" - with project_path.as_cwd({'HATCH_QUIET': '1'}): + with project_path.as_cwd({"HATCH_QUIET": "1"}): artifacts = list(builder.build()) subprocess_run.assert_called_once_with( - ['cargo', 'install', 'pyapp', '--force', '--root', mocker.ANY], + ["cargo", "install", "pyapp", "--force", "--root", mocker.ANY], cwd=mocker.ANY, - env=ExpectedEnvVars({'PYAPP_PROJECT_NAME': 'my-app', 'PYAPP_PROJECT_VERSION': '0.1.0'}), + env=ExpectedEnvVars({"PYAPP_PROJECT_NAME": "my-app", "PYAPP_PROJECT_VERSION": "0.1.0"}), stdout=subprocess.PIPE, stderr=subprocess.STDOUT, ) @@ -604,38 +604,38 @@ def test_verbosity(self, hatch, temp_dir, mocker): build_artifacts = list(build_path.iterdir()) assert len(build_artifacts) == 1 assert expected_artifact == str(build_artifacts[0]) - assert (build_path / 'binary' / ('my-app-0.1.0.exe' if sys.platform == 'win32' else 'my-app-0.1.0')).is_file() + assert (build_path / "binary" / ("my-app-0.1.0.exe" if sys.platform == "win32" else "my-app-0.1.0")).is_file() def test_local_build_with_build_target(self, hatch, temp_dir, mocker): - subprocess_run = mocker.patch('subprocess.run', side_effect=cargo_install) + subprocess_run = mocker.patch("subprocess.run", side_effect=cargo_install) - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' + project_path = temp_dir / "my-app" config = { - 'project': {'name': project_name, 'version': '0.1.0'}, - 'tool': { - 'hatch': { - 'build': {'targets': {'binary': {'versions': ['bootstrap']}}}, + "project": {"name": project_name, "version": "0.1.0"}, + "tool": { + "hatch": { + "build": {"targets": {"binary": {"versions": ["bootstrap"]}}}, }, }, } builder = BinaryBuilder(str(project_path), config=config) - build_path = project_path / 'dist' + build_path = project_path / "dist" - with project_path.as_cwd({'PYAPP_REPO': 'test-path', 'CARGO_BUILD_TARGET': 'target'}): + with project_path.as_cwd({"PYAPP_REPO": "test-path", "CARGO_BUILD_TARGET": "target"}): artifacts = list(builder.build()) subprocess_run.assert_called_once_with( - ['cargo', 'build', '--release', '--target-dir', mocker.ANY], - cwd='test-path', - env=ExpectedEnvVars({'PYAPP_PROJECT_NAME': 'my-app', 'PYAPP_PROJECT_VERSION': '0.1.0'}), + ["cargo", "build", "--release", "--target-dir", mocker.ANY], + cwd="test-path", + env=ExpectedEnvVars({"PYAPP_PROJECT_NAME": "my-app", "PYAPP_PROJECT_VERSION": "0.1.0"}), ) assert len(artifacts) == 1 @@ -645,39 +645,39 @@ def test_local_build_with_build_target(self, hatch, temp_dir, mocker): assert len(build_artifacts) == 1 assert expected_artifact == str(build_artifacts[0]) assert ( - build_path / 'binary' / ('my-app-0.1.0-target.exe' if sys.platform == 'win32' else 'my-app-0.1.0-target') + build_path / "binary" / ("my-app-0.1.0-target.exe" if sys.platform == "win32" else "my-app-0.1.0-target") ).is_file() def test_local_build_no_build_target(self, hatch, temp_dir, mocker): - subprocess_run = mocker.patch('subprocess.run', side_effect=cargo_install) + subprocess_run = mocker.patch("subprocess.run", side_effect=cargo_install) - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' + project_path = temp_dir / "my-app" config = { - 'project': {'name': project_name, 'version': '0.1.0'}, - 'tool': { - 'hatch': { - 'build': {'targets': {'binary': {'versions': ['bootstrap']}}}, + "project": {"name": project_name, "version": "0.1.0"}, + "tool": { + "hatch": { + "build": {"targets": {"binary": {"versions": ["bootstrap"]}}}, }, }, } builder = BinaryBuilder(str(project_path), config=config) - build_path = project_path / 'dist' + build_path = project_path / "dist" - with project_path.as_cwd({'PYAPP_REPO': 'test-path'}): + with project_path.as_cwd({"PYAPP_REPO": "test-path"}): artifacts = list(builder.build()) subprocess_run.assert_called_once_with( - ['cargo', 'build', '--release', '--target-dir', mocker.ANY], - cwd='test-path', - env=ExpectedEnvVars({'PYAPP_PROJECT_NAME': 'my-app', 'PYAPP_PROJECT_VERSION': '0.1.0'}), + ["cargo", "build", "--release", "--target-dir", mocker.ANY], + cwd="test-path", + env=ExpectedEnvVars({"PYAPP_PROJECT_NAME": "my-app", "PYAPP_PROJECT_VERSION": "0.1.0"}), ) assert len(artifacts) == 1 @@ -686,38 +686,38 @@ def test_local_build_no_build_target(self, hatch, temp_dir, mocker): build_artifacts = list(build_path.iterdir()) assert len(build_artifacts) == 1 assert expected_artifact == str(build_artifacts[0]) - assert (build_path / 'binary' / ('my-app-0.1.0.exe' if sys.platform == 'win32' else 'my-app-0.1.0')).is_file() + assert (build_path / "binary" / ("my-app-0.1.0.exe" if sys.platform == "win32" else "my-app-0.1.0")).is_file() def test_legacy(self, hatch, temp_dir, mocker): - subprocess_run = mocker.patch('subprocess.run', side_effect=cargo_install) + subprocess_run = mocker.patch("subprocess.run", side_effect=cargo_install) - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' + project_path = temp_dir / "my-app" config = { - 'project': {'name': project_name, 'version': '0.1.0'}, - 'tool': { - 'hatch': { - 'build': {'targets': {'app': {'versions': ['bootstrap']}}}, + "project": {"name": project_name, "version": "0.1.0"}, + "tool": { + "hatch": { + "build": {"targets": {"app": {"versions": ["bootstrap"]}}}, }, }, } builder = AppBuilder(str(project_path), config=config) - build_path = project_path / 'dist' + build_path = project_path / "dist" with project_path.as_cwd(): artifacts = list(builder.build()) subprocess_run.assert_called_once_with( - ['cargo', 'install', 'pyapp', '--force', '--root', mocker.ANY], + ["cargo", "install", "pyapp", "--force", "--root", mocker.ANY], cwd=mocker.ANY, - env=ExpectedEnvVars({'PYAPP_PROJECT_NAME': 'my-app', 'PYAPP_PROJECT_VERSION': '0.1.0'}), + env=ExpectedEnvVars({"PYAPP_PROJECT_NAME": "my-app", "PYAPP_PROJECT_VERSION": "0.1.0"}), ) assert len(artifacts) == 1 @@ -726,4 +726,4 @@ def test_legacy(self, hatch, temp_dir, mocker): build_artifacts = list(build_path.iterdir()) assert len(build_artifacts) == 1 assert expected_artifact == str(build_artifacts[0]) - assert (build_path / 'app' / ('my-app-0.1.0.exe' if sys.platform == 'win32' else 'my-app-0.1.0')).is_file() + assert (build_path / "app" / ("my-app-0.1.0.exe" if sys.platform == "win32" else "my-app-0.1.0")).is_file() diff --git a/tests/backend/builders/test_config.py b/tests/backend/builders/test_config.py index 0fb358e04..a0d03f9f4 100644 --- a/tests/backend/builders/test_config.py +++ b/tests/backend/builders/test_config.py @@ -15,50 +15,50 @@ class TestDirectory: def test_default(self, isolation): builder = MockBuilder(str(isolation)) - assert builder.config.directory == builder.config.directory == str(isolation / 'dist') + assert builder.config.directory == builder.config.directory == str(isolation / "dist") def test_target(self, isolation): - config = {'tool': {'hatch': {'build': {'targets': {'foo': {'directory': 'bar'}}}}}} + config = {"tool": {"hatch": {"build": {"targets": {"foo": {"directory": "bar"}}}}}} builder = MockBuilder(str(isolation), config=config) - builder.PLUGIN_NAME = 'foo' + builder.PLUGIN_NAME = "foo" - assert builder.config.directory == str(isolation / 'bar') + assert builder.config.directory == str(isolation / "bar") def test_target_not_boolean(self, isolation): - config = {'tool': {'hatch': {'build': {'targets': {'foo': {'directory': 9000}}}}}} + config = {"tool": {"hatch": {"build": {"targets": {"foo": {"directory": 9000}}}}}} builder = MockBuilder(str(isolation), config=config) - builder.PLUGIN_NAME = 'foo' + builder.PLUGIN_NAME = "foo" - with pytest.raises(TypeError, match='Field `tool.hatch.build.targets.foo.directory` must be a string'): + with pytest.raises(TypeError, match="Field `tool.hatch.build.targets.foo.directory` must be a string"): _ = builder.config.directory def test_global(self, isolation): - config = {'tool': {'hatch': {'build': {'directory': 'bar'}}}} + config = {"tool": {"hatch": {"build": {"directory": "bar"}}}} builder = MockBuilder(str(isolation), config=config) - builder.PLUGIN_NAME = 'foo' + builder.PLUGIN_NAME = "foo" - assert builder.config.directory == str(isolation / 'bar') + assert builder.config.directory == str(isolation / "bar") def test_global_not_boolean(self, isolation): - config = {'tool': {'hatch': {'build': {'directory': 9000}}}} + config = {"tool": {"hatch": {"build": {"directory": 9000}}}} builder = MockBuilder(str(isolation), config=config) - builder.PLUGIN_NAME = 'foo' + builder.PLUGIN_NAME = "foo" - with pytest.raises(TypeError, match='Field `tool.hatch.build.directory` must be a string'): + with pytest.raises(TypeError, match="Field `tool.hatch.build.directory` must be a string"): _ = builder.config.directory def test_target_overrides_global(self, isolation): - config = {'tool': {'hatch': {'build': {'directory': 'bar', 'targets': {'foo': {'directory': 'baz'}}}}}} + config = {"tool": {"hatch": {"build": {"directory": "bar", "targets": {"foo": {"directory": "baz"}}}}}} builder = MockBuilder(str(isolation), config=config) - builder.PLUGIN_NAME = 'foo' + builder.PLUGIN_NAME = "foo" - assert builder.config.directory == str(isolation / 'baz') + assert builder.config.directory == str(isolation / "baz") def test_absolute_path(self, isolation): absolute_path = str(isolation) - config = {'tool': {'hatch': {'build': {'targets': {'foo': {'directory': absolute_path}}}}}} + config = {"tool": {"hatch": {"build": {"targets": {"foo": {"directory": absolute_path}}}}}} builder = MockBuilder(absolute_path, config=config) - builder.PLUGIN_NAME = 'foo' + builder.PLUGIN_NAME = "foo" assert builder.config.directory == absolute_path @@ -70,45 +70,45 @@ def test_default(self, isolation): assert builder.config.skip_excluded_dirs is builder.config.skip_excluded_dirs is False def test_target(self, isolation): - config = {'tool': {'hatch': {'build': {'targets': {'foo': {'skip-excluded-dirs': True}}}}}} + config = {"tool": {"hatch": {"build": {"targets": {"foo": {"skip-excluded-dirs": True}}}}}} builder = MockBuilder(str(isolation), config=config) - builder.PLUGIN_NAME = 'foo' + builder.PLUGIN_NAME = "foo" assert builder.config.skip_excluded_dirs is True def test_target_not_boolean(self, isolation): - config = {'tool': {'hatch': {'build': {'targets': {'foo': {'skip-excluded-dirs': 9000}}}}}} + config = {"tool": {"hatch": {"build": {"targets": {"foo": {"skip-excluded-dirs": 9000}}}}}} builder = MockBuilder(str(isolation), config=config) - builder.PLUGIN_NAME = 'foo' + builder.PLUGIN_NAME = "foo" with pytest.raises( - TypeError, match='Field `tool.hatch.build.targets.foo.skip-excluded-dirs` must be a boolean' + TypeError, match="Field `tool.hatch.build.targets.foo.skip-excluded-dirs` must be a boolean" ): _ = builder.config.skip_excluded_dirs def test_global(self, isolation): - config = {'tool': {'hatch': {'build': {'skip-excluded-dirs': True}}}} + config = {"tool": {"hatch": {"build": {"skip-excluded-dirs": True}}}} builder = MockBuilder(str(isolation), config=config) - builder.PLUGIN_NAME = 'foo' + builder.PLUGIN_NAME = "foo" assert builder.config.skip_excluded_dirs is True def test_global_not_boolean(self, isolation): - config = {'tool': {'hatch': {'build': {'skip-excluded-dirs': 9000}}}} + config = {"tool": {"hatch": {"build": {"skip-excluded-dirs": 9000}}}} builder = MockBuilder(str(isolation), config=config) - builder.PLUGIN_NAME = 'foo' + builder.PLUGIN_NAME = "foo" - with pytest.raises(TypeError, match='Field `tool.hatch.build.skip-excluded-dirs` must be a boolean'): + with pytest.raises(TypeError, match="Field `tool.hatch.build.skip-excluded-dirs` must be a boolean"): _ = builder.config.skip_excluded_dirs def test_target_overrides_global(self, isolation): config = { - 'tool': { - 'hatch': {'build': {'skip-excluded-dirs': True, 'targets': {'foo': {'skip-excluded-dirs': False}}}} + "tool": { + "hatch": {"build": {"skip-excluded-dirs": True, "targets": {"foo": {"skip-excluded-dirs": False}}}} } } builder = MockBuilder(str(isolation), config=config) - builder.PLUGIN_NAME = 'foo' + builder.PLUGIN_NAME = "foo" assert builder.config.skip_excluded_dirs is False @@ -120,39 +120,39 @@ def test_default(self, isolation): assert builder.config.ignore_vcs is builder.config.ignore_vcs is False def test_target(self, isolation): - config = {'tool': {'hatch': {'build': {'targets': {'foo': {'ignore-vcs': True}}}}}} + config = {"tool": {"hatch": {"build": {"targets": {"foo": {"ignore-vcs": True}}}}}} builder = MockBuilder(str(isolation), config=config) - builder.PLUGIN_NAME = 'foo' + builder.PLUGIN_NAME = "foo" assert builder.config.ignore_vcs is True def test_target_not_boolean(self, isolation): - config = {'tool': {'hatch': {'build': {'targets': {'foo': {'ignore-vcs': 9000}}}}}} + config = {"tool": {"hatch": {"build": {"targets": {"foo": {"ignore-vcs": 9000}}}}}} builder = MockBuilder(str(isolation), config=config) - builder.PLUGIN_NAME = 'foo' + builder.PLUGIN_NAME = "foo" - with pytest.raises(TypeError, match='Field `tool.hatch.build.targets.foo.ignore-vcs` must be a boolean'): + with pytest.raises(TypeError, match="Field `tool.hatch.build.targets.foo.ignore-vcs` must be a boolean"): _ = builder.config.ignore_vcs def test_global(self, isolation): - config = {'tool': {'hatch': {'build': {'ignore-vcs': True}}}} + config = {"tool": {"hatch": {"build": {"ignore-vcs": True}}}} builder = MockBuilder(str(isolation), config=config) - builder.PLUGIN_NAME = 'foo' + builder.PLUGIN_NAME = "foo" assert builder.config.ignore_vcs is True def test_global_not_boolean(self, isolation): - config = {'tool': {'hatch': {'build': {'ignore-vcs': 9000}}}} + config = {"tool": {"hatch": {"build": {"ignore-vcs": 9000}}}} builder = MockBuilder(str(isolation), config=config) - builder.PLUGIN_NAME = 'foo' + builder.PLUGIN_NAME = "foo" - with pytest.raises(TypeError, match='Field `tool.hatch.build.ignore-vcs` must be a boolean'): + with pytest.raises(TypeError, match="Field `tool.hatch.build.ignore-vcs` must be a boolean"): _ = builder.config.ignore_vcs def test_target_overrides_global(self, isolation): - config = {'tool': {'hatch': {'build': {'ignore-vcs': True, 'targets': {'foo': {'ignore-vcs': False}}}}}} + config = {"tool": {"hatch": {"build": {"ignore-vcs": True, "targets": {"foo": {"ignore-vcs": False}}}}}} builder = MockBuilder(str(isolation), config=config) - builder.PLUGIN_NAME = 'foo' + builder.PLUGIN_NAME = "foo" assert builder.config.ignore_vcs is False @@ -164,51 +164,51 @@ def test_default(self, isolation): assert builder.config.require_runtime_dependencies is builder.config.require_runtime_dependencies is False def test_target(self, isolation): - config = {'tool': {'hatch': {'build': {'targets': {'foo': {'require-runtime-dependencies': True}}}}}} + config = {"tool": {"hatch": {"build": {"targets": {"foo": {"require-runtime-dependencies": True}}}}}} builder = MockBuilder(str(isolation), config=config) - builder.PLUGIN_NAME = 'foo' + builder.PLUGIN_NAME = "foo" assert builder.config.require_runtime_dependencies is True def test_target_not_boolean(self, isolation): - config = {'tool': {'hatch': {'build': {'targets': {'foo': {'require-runtime-dependencies': 9000}}}}}} + config = {"tool": {"hatch": {"build": {"targets": {"foo": {"require-runtime-dependencies": 9000}}}}}} builder = MockBuilder(str(isolation), config=config) - builder.PLUGIN_NAME = 'foo' + builder.PLUGIN_NAME = "foo" with pytest.raises( TypeError, - match='Field `tool.hatch.build.targets.foo.require-runtime-dependencies` must be a boolean', + match="Field `tool.hatch.build.targets.foo.require-runtime-dependencies` must be a boolean", ): _ = builder.config.require_runtime_dependencies def test_global(self, isolation): - config = {'tool': {'hatch': {'build': {'require-runtime-dependencies': True}}}} + config = {"tool": {"hatch": {"build": {"require-runtime-dependencies": True}}}} builder = MockBuilder(str(isolation), config=config) - builder.PLUGIN_NAME = 'foo' + builder.PLUGIN_NAME = "foo" assert builder.config.require_runtime_dependencies is True def test_global_not_boolean(self, isolation): - config = {'tool': {'hatch': {'build': {'require-runtime-dependencies': 9000}}}} + config = {"tool": {"hatch": {"build": {"require-runtime-dependencies": 9000}}}} builder = MockBuilder(str(isolation), config=config) - builder.PLUGIN_NAME = 'foo' + builder.PLUGIN_NAME = "foo" - with pytest.raises(TypeError, match='Field `tool.hatch.build.require-runtime-dependencies` must be a boolean'): + with pytest.raises(TypeError, match="Field `tool.hatch.build.require-runtime-dependencies` must be a boolean"): _ = builder.config.require_runtime_dependencies def test_target_overrides_global(self, isolation): config = { - 'tool': { - 'hatch': { - 'build': { - 'require-runtime-dependencies': True, - 'targets': {'foo': {'require-runtime-dependencies': False}}, + "tool": { + "hatch": { + "build": { + "require-runtime-dependencies": True, + "targets": {"foo": {"require-runtime-dependencies": False}}, } } } } builder = MockBuilder(str(isolation), config=config) - builder.PLUGIN_NAME = 'foo' + builder.PLUGIN_NAME = "foo" assert builder.config.require_runtime_dependencies is False @@ -221,132 +221,132 @@ def test_default(self, isolation): def test_target(self, isolation): config = { - 'project': {'name': 'my_app', 'version': '0.0.1', 'optional-dependencies': {'foo': [], 'bar': []}}, - 'tool': {'hatch': {'build': {'targets': {'foo': {'require-runtime-features': ['foo', 'bar']}}}}}, + "project": {"name": "my_app", "version": "0.0.1", "optional-dependencies": {"foo": [], "bar": []}}, + "tool": {"hatch": {"build": {"targets": {"foo": {"require-runtime-features": ["foo", "bar"]}}}}}, } builder = MockBuilder(str(isolation), config=config) - builder.PLUGIN_NAME = 'foo' + builder.PLUGIN_NAME = "foo" - assert builder.config.require_runtime_features == ['foo', 'bar'] + assert builder.config.require_runtime_features == ["foo", "bar"] def test_target_not_array(self, isolation): - config = {'tool': {'hatch': {'build': {'targets': {'foo': {'require-runtime-features': 9000}}}}}} + config = {"tool": {"hatch": {"build": {"targets": {"foo": {"require-runtime-features": 9000}}}}}} builder = MockBuilder(str(isolation), config=config) - builder.PLUGIN_NAME = 'foo' + builder.PLUGIN_NAME = "foo" with pytest.raises( - TypeError, match='Field `tool.hatch.build.targets.foo.require-runtime-features` must be an array' + TypeError, match="Field `tool.hatch.build.targets.foo.require-runtime-features` must be an array" ): _ = builder.config.require_runtime_features def test_target_feature_not_string(self, isolation): - config = {'tool': {'hatch': {'build': {'targets': {'foo': {'require-runtime-features': [9000]}}}}}} + config = {"tool": {"hatch": {"build": {"targets": {"foo": {"require-runtime-features": [9000]}}}}}} builder = MockBuilder(str(isolation), config=config) - builder.PLUGIN_NAME = 'foo' + builder.PLUGIN_NAME = "foo" with pytest.raises( TypeError, - match='Feature #1 of field `tool.hatch.build.targets.foo.require-runtime-features` must be a string', + match="Feature #1 of field `tool.hatch.build.targets.foo.require-runtime-features` must be a string", ): _ = builder.config.require_runtime_features def test_target_feature_empty_string(self, isolation): - config = {'tool': {'hatch': {'build': {'targets': {'foo': {'require-runtime-features': ['']}}}}}} + config = {"tool": {"hatch": {"build": {"targets": {"foo": {"require-runtime-features": [""]}}}}}} builder = MockBuilder(str(isolation), config=config) - builder.PLUGIN_NAME = 'foo' + builder.PLUGIN_NAME = "foo" with pytest.raises( ValueError, match=( - 'Feature #1 of field `tool.hatch.build.targets.foo.require-runtime-features` cannot be an empty string' + "Feature #1 of field `tool.hatch.build.targets.foo.require-runtime-features` cannot be an empty string" ), ): _ = builder.config.require_runtime_features def test_target_feature_unknown(self, isolation): config = { - 'project': {'name': 'my_app', 'version': '0.0.1'}, - 'tool': {'hatch': {'build': {'targets': {'foo': {'require-runtime-features': ['foo_bar']}}}}}, + "project": {"name": "my_app", "version": "0.0.1"}, + "tool": {"hatch": {"build": {"targets": {"foo": {"require-runtime-features": ["foo_bar"]}}}}}, } builder = MockBuilder(str(isolation), config=config) - builder.PLUGIN_NAME = 'foo' + builder.PLUGIN_NAME = "foo" with pytest.raises( ValueError, match=( - 'Feature `foo-bar` of field `tool.hatch.build.targets.foo.require-runtime-features` is not defined in ' - 'field `project.optional-dependencies`' + "Feature `foo-bar` of field `tool.hatch.build.targets.foo.require-runtime-features` is not defined in " + "field `project.optional-dependencies`" ), ): _ = builder.config.require_runtime_features def test_global(self, isolation): config = { - 'project': {'name': 'my_app', 'version': '0.0.1', 'optional-dependencies': {'foo': [], 'bar': []}}, - 'tool': {'hatch': {'build': {'require-runtime-features': ['foo', 'bar']}}}, + "project": {"name": "my_app", "version": "0.0.1", "optional-dependencies": {"foo": [], "bar": []}}, + "tool": {"hatch": {"build": {"require-runtime-features": ["foo", "bar"]}}}, } builder = MockBuilder(str(isolation), config=config) - assert builder.config.require_runtime_features == ['foo', 'bar'] + assert builder.config.require_runtime_features == ["foo", "bar"] def test_global_not_array(self, isolation): - config = {'tool': {'hatch': {'build': {'require-runtime-features': 9000}}}} + config = {"tool": {"hatch": {"build": {"require-runtime-features": 9000}}}} builder = MockBuilder(str(isolation), config=config) - with pytest.raises(TypeError, match='Field `tool.hatch.build.require-runtime-features` must be an array'): + with pytest.raises(TypeError, match="Field `tool.hatch.build.require-runtime-features` must be an array"): _ = builder.config.require_runtime_features def test_global_feature_not_string(self, isolation): - config = {'tool': {'hatch': {'build': {'require-runtime-features': [9000]}}}} + config = {"tool": {"hatch": {"build": {"require-runtime-features": [9000]}}}} builder = MockBuilder(str(isolation), config=config) with pytest.raises( - TypeError, match='Feature #1 of field `tool.hatch.build.require-runtime-features` must be a string' + TypeError, match="Feature #1 of field `tool.hatch.build.require-runtime-features` must be a string" ): _ = builder.config.require_runtime_features def test_global_feature_empty_string(self, isolation): - config = {'tool': {'hatch': {'build': {'require-runtime-features': ['']}}}} + config = {"tool": {"hatch": {"build": {"require-runtime-features": [""]}}}} builder = MockBuilder(str(isolation), config=config) with pytest.raises( ValueError, - match='Feature #1 of field `tool.hatch.build.require-runtime-features` cannot be an empty string', + match="Feature #1 of field `tool.hatch.build.require-runtime-features` cannot be an empty string", ): _ = builder.config.require_runtime_features def test_global_feature_unknown(self, isolation): config = { - 'project': {'name': 'my_app', 'version': '0.0.1'}, - 'tool': {'hatch': {'build': {'require-runtime-features': ['foo_bar']}}}, + "project": {"name": "my_app", "version": "0.0.1"}, + "tool": {"hatch": {"build": {"require-runtime-features": ["foo_bar"]}}}, } builder = MockBuilder(str(isolation), config=config) with pytest.raises( ValueError, match=( - 'Feature `foo-bar` of field `tool.hatch.build.require-runtime-features` is not defined in ' - 'field `project.optional-dependencies`' + "Feature `foo-bar` of field `tool.hatch.build.require-runtime-features` is not defined in " + "field `project.optional-dependencies`" ), ): _ = builder.config.require_runtime_features def test_target_overrides_global(self, isolation): config = { - 'project': {'name': 'my_app', 'version': '0.0.1', 'optional-dependencies': {'foo_bar': [], 'bar_baz': []}}, - 'tool': { - 'hatch': { - 'build': { - 'require-runtime-features': ['bar_baz'], - 'targets': {'foo': {'require-runtime-features': ['foo_bar']}}, + "project": {"name": "my_app", "version": "0.0.1", "optional-dependencies": {"foo_bar": [], "bar_baz": []}}, + "tool": { + "hatch": { + "build": { + "require-runtime-features": ["bar_baz"], + "targets": {"foo": {"require-runtime-features": ["foo_bar"]}}, } } }, } builder = MockBuilder(str(isolation), config=config) - builder.PLUGIN_NAME = 'foo' + builder.PLUGIN_NAME = "foo" - assert builder.config.require_runtime_features == ['foo-bar'] + assert builder.config.require_runtime_features == ["foo-bar"] class TestOnlyPackages: @@ -356,39 +356,39 @@ def test_default(self, isolation): assert builder.config.only_packages is builder.config.only_packages is False def test_target(self, isolation): - config = {'tool': {'hatch': {'build': {'targets': {'foo': {'only-packages': True}}}}}} + config = {"tool": {"hatch": {"build": {"targets": {"foo": {"only-packages": True}}}}}} builder = MockBuilder(str(isolation), config=config) - builder.PLUGIN_NAME = 'foo' + builder.PLUGIN_NAME = "foo" assert builder.config.only_packages is True def test_target_not_boolean(self, isolation): - config = {'tool': {'hatch': {'build': {'targets': {'foo': {'only-packages': 9000}}}}}} + config = {"tool": {"hatch": {"build": {"targets": {"foo": {"only-packages": 9000}}}}}} builder = MockBuilder(str(isolation), config=config) - builder.PLUGIN_NAME = 'foo' + builder.PLUGIN_NAME = "foo" - with pytest.raises(TypeError, match='Field `tool.hatch.build.targets.foo.only-packages` must be a boolean'): + with pytest.raises(TypeError, match="Field `tool.hatch.build.targets.foo.only-packages` must be a boolean"): _ = builder.config.only_packages def test_global(self, isolation): - config = {'tool': {'hatch': {'build': {'only-packages': True}}}} + config = {"tool": {"hatch": {"build": {"only-packages": True}}}} builder = MockBuilder(str(isolation), config=config) - builder.PLUGIN_NAME = 'foo' + builder.PLUGIN_NAME = "foo" assert builder.config.only_packages is True def test_global_not_boolean(self, isolation): - config = {'tool': {'hatch': {'build': {'only-packages': 9000}}}} + config = {"tool": {"hatch": {"build": {"only-packages": 9000}}}} builder = MockBuilder(str(isolation), config=config) - builder.PLUGIN_NAME = 'foo' + builder.PLUGIN_NAME = "foo" - with pytest.raises(TypeError, match='Field `tool.hatch.build.only-packages` must be a boolean'): + with pytest.raises(TypeError, match="Field `tool.hatch.build.only-packages` must be a boolean"): _ = builder.config.only_packages def test_target_overrides_global(self, isolation): - config = {'tool': {'hatch': {'build': {'only-packages': True, 'targets': {'foo': {'only-packages': False}}}}}} + config = {"tool": {"hatch": {"build": {"only-packages": True, "targets": {"foo": {"only-packages": False}}}}}} builder = MockBuilder(str(isolation), config=config) - builder.PLUGIN_NAME = 'foo' + builder.PLUGIN_NAME = "foo" assert builder.config.only_packages is False @@ -400,39 +400,39 @@ def test_default(self, isolation): assert builder.config.reproducible is builder.config.reproducible is True def test_target(self, isolation): - config = {'tool': {'hatch': {'build': {'targets': {'foo': {'reproducible': False}}}}}} + config = {"tool": {"hatch": {"build": {"targets": {"foo": {"reproducible": False}}}}}} builder = MockBuilder(str(isolation), config=config) - builder.PLUGIN_NAME = 'foo' + builder.PLUGIN_NAME = "foo" assert builder.config.reproducible is False def test_target_not_boolean(self, isolation): - config = {'tool': {'hatch': {'build': {'targets': {'foo': {'reproducible': 9000}}}}}} + config = {"tool": {"hatch": {"build": {"targets": {"foo": {"reproducible": 9000}}}}}} builder = MockBuilder(str(isolation), config=config) - builder.PLUGIN_NAME = 'foo' + builder.PLUGIN_NAME = "foo" - with pytest.raises(TypeError, match='Field `tool.hatch.build.targets.foo.reproducible` must be a boolean'): + with pytest.raises(TypeError, match="Field `tool.hatch.build.targets.foo.reproducible` must be a boolean"): _ = builder.config.reproducible def test_global(self, isolation): - config = {'tool': {'hatch': {'build': {'reproducible': False}}}} + config = {"tool": {"hatch": {"build": {"reproducible": False}}}} builder = MockBuilder(str(isolation), config=config) - builder.PLUGIN_NAME = 'foo' + builder.PLUGIN_NAME = "foo" assert builder.config.reproducible is False def test_global_not_boolean(self, isolation): - config = {'tool': {'hatch': {'build': {'reproducible': 9000}}}} + config = {"tool": {"hatch": {"build": {"reproducible": 9000}}}} builder = MockBuilder(str(isolation), config=config) - builder.PLUGIN_NAME = 'foo' + builder.PLUGIN_NAME = "foo" - with pytest.raises(TypeError, match='Field `tool.hatch.build.reproducible` must be a boolean'): + with pytest.raises(TypeError, match="Field `tool.hatch.build.reproducible` must be a boolean"): _ = builder.config.reproducible def test_target_overrides_global(self, isolation): - config = {'tool': {'hatch': {'build': {'reproducible': False, 'targets': {'foo': {'reproducible': True}}}}}} + config = {"tool": {"hatch": {"build": {"reproducible": False, "targets": {"foo": {"reproducible": True}}}}}} builder = MockBuilder(str(isolation), config=config) - builder.PLUGIN_NAME = 'foo' + builder.PLUGIN_NAME = "foo" assert builder.config.reproducible is True @@ -444,70 +444,70 @@ def test_default(self, isolation): assert builder.config.dev_mode_dirs == builder.config.dev_mode_dirs == [] def test_global_invalid_type(self, isolation): - config = {'tool': {'hatch': {'build': {'dev-mode-dirs': ''}}}} + config = {"tool": {"hatch": {"build": {"dev-mode-dirs": ""}}}} builder = MockBuilder(str(isolation), config=config) - with pytest.raises(TypeError, match='Field `tool.hatch.build.dev-mode-dirs` must be an array of strings'): + with pytest.raises(TypeError, match="Field `tool.hatch.build.dev-mode-dirs` must be an array of strings"): _ = builder.config.dev_mode_dirs def test_global(self, isolation): - config = {'tool': {'hatch': {'build': {'dev-mode-dirs': ['foo', 'bar/baz']}}}} + config = {"tool": {"hatch": {"build": {"dev-mode-dirs": ["foo", "bar/baz"]}}}} builder = MockBuilder(str(isolation), config=config) - assert builder.config.dev_mode_dirs == ['foo', 'bar/baz'] + assert builder.config.dev_mode_dirs == ["foo", "bar/baz"] def test_global_pattern_not_string(self, isolation): - config = {'tool': {'hatch': {'build': {'dev-mode-dirs': [0]}}}} + config = {"tool": {"hatch": {"build": {"dev-mode-dirs": [0]}}}} builder = MockBuilder(str(isolation), config=config) - with pytest.raises(TypeError, match='Directory #1 in field `tool.hatch.build.dev-mode-dirs` must be a string'): + with pytest.raises(TypeError, match="Directory #1 in field `tool.hatch.build.dev-mode-dirs` must be a string"): _ = builder.config.dev_mode_dirs def test_global_pattern_empty_string(self, isolation): - config = {'tool': {'hatch': {'build': {'dev-mode-dirs': ['']}}}} + config = {"tool": {"hatch": {"build": {"dev-mode-dirs": [""]}}}} builder = MockBuilder(str(isolation), config=config) with pytest.raises( - ValueError, match='Directory #1 in field `tool.hatch.build.dev-mode-dirs` cannot be an empty string' + ValueError, match="Directory #1 in field `tool.hatch.build.dev-mode-dirs` cannot be an empty string" ): _ = builder.config.dev_mode_dirs def test_target(self, isolation): - config = {'tool': {'hatch': {'build': {'targets': {'foo': {'dev-mode-dirs': ['foo', 'bar/baz']}}}}}} + config = {"tool": {"hatch": {"build": {"targets": {"foo": {"dev-mode-dirs": ["foo", "bar/baz"]}}}}}} builder = MockBuilder(str(isolation), config=config) - builder.PLUGIN_NAME = 'foo' + builder.PLUGIN_NAME = "foo" - assert builder.config.dev_mode_dirs == ['foo', 'bar/baz'] + assert builder.config.dev_mode_dirs == ["foo", "bar/baz"] def test_target_pattern_not_string(self, isolation): - config = {'tool': {'hatch': {'build': {'targets': {'foo': {'dev-mode-dirs': [0]}}}}}} + config = {"tool": {"hatch": {"build": {"targets": {"foo": {"dev-mode-dirs": [0]}}}}}} builder = MockBuilder(str(isolation), config=config) - builder.PLUGIN_NAME = 'foo' + builder.PLUGIN_NAME = "foo" with pytest.raises( - TypeError, match='Directory #1 in field `tool.hatch.build.targets.foo.dev-mode-dirs` must be a string' + TypeError, match="Directory #1 in field `tool.hatch.build.targets.foo.dev-mode-dirs` must be a string" ): _ = builder.config.dev_mode_dirs def test_target_pattern_empty_string(self, isolation): - config = {'tool': {'hatch': {'build': {'targets': {'foo': {'dev-mode-dirs': ['']}}}}}} + config = {"tool": {"hatch": {"build": {"targets": {"foo": {"dev-mode-dirs": [""]}}}}}} builder = MockBuilder(str(isolation), config=config) - builder.PLUGIN_NAME = 'foo' + builder.PLUGIN_NAME = "foo" with pytest.raises( ValueError, - match='Directory #1 in field `tool.hatch.build.targets.foo.dev-mode-dirs` cannot be an empty string', + match="Directory #1 in field `tool.hatch.build.targets.foo.dev-mode-dirs` cannot be an empty string", ): _ = builder.config.dev_mode_dirs def test_target_overrides_global(self, isolation): config = { - 'tool': {'hatch': {'build': {'dev-mode-dirs': ['foo'], 'targets': {'foo': {'dev-mode-dirs': ['bar']}}}}} + "tool": {"hatch": {"build": {"dev-mode-dirs": ["foo"], "targets": {"foo": {"dev-mode-dirs": ["bar"]}}}}} } builder = MockBuilder(str(isolation), config=config) - builder.PLUGIN_NAME = 'foo' + builder.PLUGIN_NAME = "foo" - assert builder.config.dev_mode_dirs == ['bar'] + assert builder.config.dev_mode_dirs == ["bar"] class TestDevModeExact: @@ -517,39 +517,39 @@ def test_default(self, isolation): assert builder.config.dev_mode_exact is builder.config.dev_mode_exact is False def test_target(self, isolation): - config = {'tool': {'hatch': {'build': {'targets': {'foo': {'dev-mode-exact': True}}}}}} + config = {"tool": {"hatch": {"build": {"targets": {"foo": {"dev-mode-exact": True}}}}}} builder = MockBuilder(str(isolation), config=config) - builder.PLUGIN_NAME = 'foo' + builder.PLUGIN_NAME = "foo" assert builder.config.dev_mode_exact is True def test_target_not_boolean(self, isolation): - config = {'tool': {'hatch': {'build': {'targets': {'foo': {'dev-mode-exact': 9000}}}}}} + config = {"tool": {"hatch": {"build": {"targets": {"foo": {"dev-mode-exact": 9000}}}}}} builder = MockBuilder(str(isolation), config=config) - builder.PLUGIN_NAME = 'foo' + builder.PLUGIN_NAME = "foo" - with pytest.raises(TypeError, match='Field `tool.hatch.build.targets.foo.dev-mode-exact` must be a boolean'): + with pytest.raises(TypeError, match="Field `tool.hatch.build.targets.foo.dev-mode-exact` must be a boolean"): _ = builder.config.dev_mode_exact def test_global(self, isolation): - config = {'tool': {'hatch': {'build': {'dev-mode-exact': True}}}} + config = {"tool": {"hatch": {"build": {"dev-mode-exact": True}}}} builder = MockBuilder(str(isolation), config=config) - builder.PLUGIN_NAME = 'foo' + builder.PLUGIN_NAME = "foo" assert builder.config.dev_mode_exact is True def test_global_not_boolean(self, isolation): - config = {'tool': {'hatch': {'build': {'dev-mode-exact': 9000}}}} + config = {"tool": {"hatch": {"build": {"dev-mode-exact": 9000}}}} builder = MockBuilder(str(isolation), config=config) - builder.PLUGIN_NAME = 'foo' + builder.PLUGIN_NAME = "foo" - with pytest.raises(TypeError, match='Field `tool.hatch.build.dev-mode-exact` must be a boolean'): + with pytest.raises(TypeError, match="Field `tool.hatch.build.dev-mode-exact` must be a boolean"): _ = builder.config.dev_mode_exact def test_target_overrides_global(self, isolation): - config = {'tool': {'hatch': {'build': {'dev-mode-exact': True, 'targets': {'foo': {'dev-mode-exact': False}}}}}} + config = {"tool": {"hatch": {"build": {"dev-mode-exact": True, "targets": {"foo": {"dev-mode-exact": False}}}}}} builder = MockBuilder(str(isolation), config=config) - builder.PLUGIN_NAME = 'foo' + builder.PLUGIN_NAME = "foo" assert builder.config.dev_mode_exact is False @@ -561,79 +561,79 @@ def test_default(self, isolation): assert builder.config.packages == builder.config.packages == [] def test_global_invalid_type(self, isolation): - config = {'tool': {'hatch': {'build': {'packages': ''}}}} + config = {"tool": {"hatch": {"build": {"packages": ""}}}} builder = MockBuilder(str(isolation), config=config) - with pytest.raises(TypeError, match='Field `tool.hatch.build.packages` must be an array of strings'): + with pytest.raises(TypeError, match="Field `tool.hatch.build.packages` must be an array of strings"): _ = builder.config.packages def test_global(self, isolation): - config = {'tool': {'hatch': {'build': {'packages': ['src/foo']}}}} + config = {"tool": {"hatch": {"build": {"packages": ["src/foo"]}}}} builder = MockBuilder(str(isolation), config=config) assert len(builder.config.packages) == 1 - assert builder.config.packages[0] == pjoin('src', 'foo') + assert builder.config.packages[0] == pjoin("src", "foo") def test_global_package_not_string(self, isolation): - config = {'tool': {'hatch': {'build': {'packages': [0]}}}} + config = {"tool": {"hatch": {"build": {"packages": [0]}}}} builder = MockBuilder(str(isolation), config=config) - with pytest.raises(TypeError, match='Package #1 in field `tool.hatch.build.packages` must be a string'): + with pytest.raises(TypeError, match="Package #1 in field `tool.hatch.build.packages` must be a string"): _ = builder.config.packages def test_global_package_empty_string(self, isolation): - config = {'tool': {'hatch': {'build': {'packages': ['']}}}} + config = {"tool": {"hatch": {"build": {"packages": [""]}}}} builder = MockBuilder(str(isolation), config=config) with pytest.raises( - ValueError, match='Package #1 in field `tool.hatch.build.packages` cannot be an empty string' + ValueError, match="Package #1 in field `tool.hatch.build.packages` cannot be an empty string" ): _ = builder.config.packages def test_target(self, isolation): - config = {'tool': {'hatch': {'build': {'targets': {'foo': {'packages': ['src/foo']}}}}}} + config = {"tool": {"hatch": {"build": {"targets": {"foo": {"packages": ["src/foo"]}}}}}} builder = MockBuilder(str(isolation), config=config) - builder.PLUGIN_NAME = 'foo' + builder.PLUGIN_NAME = "foo" assert len(builder.config.packages) == 1 - assert builder.config.packages[0] == pjoin('src', 'foo') + assert builder.config.packages[0] == pjoin("src", "foo") def test_target_package_not_string(self, isolation): - config = {'tool': {'hatch': {'build': {'targets': {'foo': {'packages': [0]}}}}}} + config = {"tool": {"hatch": {"build": {"targets": {"foo": {"packages": [0]}}}}}} builder = MockBuilder(str(isolation), config=config) - builder.PLUGIN_NAME = 'foo' + builder.PLUGIN_NAME = "foo" with pytest.raises( - TypeError, match='Package #1 in field `tool.hatch.build.targets.foo.packages` must be a string' + TypeError, match="Package #1 in field `tool.hatch.build.targets.foo.packages` must be a string" ): _ = builder.config.packages def test_target_package_empty_string(self, isolation): - config = {'tool': {'hatch': {'build': {'targets': {'foo': {'packages': ['']}}}}}} + config = {"tool": {"hatch": {"build": {"targets": {"foo": {"packages": [""]}}}}}} builder = MockBuilder(str(isolation), config=config) - builder.PLUGIN_NAME = 'foo' + builder.PLUGIN_NAME = "foo" with pytest.raises( - ValueError, match='Package #1 in field `tool.hatch.build.targets.foo.packages` cannot be an empty string' + ValueError, match="Package #1 in field `tool.hatch.build.targets.foo.packages` cannot be an empty string" ): _ = builder.config.packages def test_target_overrides_global(self, isolation): config = { - 'tool': {'hatch': {'build': {'packages': ['src/foo'], 'targets': {'foo': {'packages': ['pkg/foo']}}}}} + "tool": {"hatch": {"build": {"packages": ["src/foo"], "targets": {"foo": {"packages": ["pkg/foo"]}}}}} } builder = MockBuilder(str(isolation), config=config) - builder.PLUGIN_NAME = 'foo' + builder.PLUGIN_NAME = "foo" assert len(builder.config.packages) == 1 - assert builder.config.packages[0] == pjoin('pkg', 'foo') + assert builder.config.packages[0] == pjoin("pkg", "foo") def test_no_source(self, isolation): - config = {'tool': {'hatch': {'build': {'packages': ['foo']}}}} + config = {"tool": {"hatch": {"build": {"packages": ["foo"]}}}} builder = MockBuilder(str(isolation), config=config) assert len(builder.config.packages) == 1 - assert builder.config.packages[0] == pjoin('foo') + assert builder.config.packages[0] == pjoin("foo") class TestSources: @@ -641,173 +641,173 @@ def test_default(self, isolation): builder = MockBuilder(str(isolation)) assert builder.config.sources == builder.config.sources == {} - assert builder.config.get_distribution_path(pjoin('src', 'foo', 'bar.py')) == pjoin('src', 'foo', 'bar.py') + assert builder.config.get_distribution_path(pjoin("src", "foo", "bar.py")) == pjoin("src", "foo", "bar.py") def test_global_invalid_type(self, isolation): - config = {'tool': {'hatch': {'build': {'sources': ''}}}} + config = {"tool": {"hatch": {"build": {"sources": ""}}}} builder = MockBuilder(str(isolation), config=config) - with pytest.raises(TypeError, match='Field `tool.hatch.build.sources` must be a mapping or array of strings'): + with pytest.raises(TypeError, match="Field `tool.hatch.build.sources` must be a mapping or array of strings"): _ = builder.config.sources def test_global_array(self, isolation): - config = {'tool': {'hatch': {'build': {'sources': ['src']}}}} + config = {"tool": {"hatch": {"build": {"sources": ["src"]}}}} builder = MockBuilder(str(isolation), config=config) assert len(builder.config.sources) == 1 - assert builder.config.sources[pjoin('src', '')] == '' - assert builder.config.get_distribution_path(pjoin('src', 'foo', 'bar.py')) == pjoin('foo', 'bar.py') + assert builder.config.sources[pjoin("src", "")] == "" + assert builder.config.get_distribution_path(pjoin("src", "foo", "bar.py")) == pjoin("foo", "bar.py") def test_global_array_source_not_string(self, isolation): - config = {'tool': {'hatch': {'build': {'sources': [0]}}}} + config = {"tool": {"hatch": {"build": {"sources": [0]}}}} builder = MockBuilder(str(isolation), config=config) - with pytest.raises(TypeError, match='Source #1 in field `tool.hatch.build.sources` must be a string'): + with pytest.raises(TypeError, match="Source #1 in field `tool.hatch.build.sources` must be a string"): _ = builder.config.sources def test_global_array_source_empty_string(self, isolation): - config = {'tool': {'hatch': {'build': {'sources': ['']}}}} + config = {"tool": {"hatch": {"build": {"sources": [""]}}}} builder = MockBuilder(str(isolation), config=config) - with pytest.raises(ValueError, match='Source #1 in field `tool.hatch.build.sources` cannot be an empty string'): + with pytest.raises(ValueError, match="Source #1 in field `tool.hatch.build.sources` cannot be an empty string"): _ = builder.config.sources def test_global_mapping(self, isolation): - config = {'tool': {'hatch': {'build': {'sources': {'src/foo': 'renamed'}}}}} + config = {"tool": {"hatch": {"build": {"sources": {"src/foo": "renamed"}}}}} builder = MockBuilder(str(isolation), config=config) assert len(builder.config.sources) == 1 - assert builder.config.sources[pjoin('src', 'foo', '')] == pjoin('renamed', '') - assert builder.config.get_distribution_path(pjoin('src', 'foo', 'bar.py')) == pjoin('renamed', 'bar.py') + assert builder.config.sources[pjoin("src", "foo", "")] == pjoin("renamed", "") + assert builder.config.get_distribution_path(pjoin("src", "foo", "bar.py")) == pjoin("renamed", "bar.py") def test_global_mapping_source_empty_string(self, isolation): - config = {'tool': {'hatch': {'build': {'sources': {'': 'renamed'}}}}} + config = {"tool": {"hatch": {"build": {"sources": {"": "renamed"}}}}} builder = MockBuilder(str(isolation), config=config) assert len(builder.config.sources) == 1 - assert builder.config.sources[''] == pjoin('renamed', '') - assert builder.config.get_distribution_path('bar.py') == pjoin('renamed', 'bar.py') - assert builder.config.get_distribution_path(pjoin('foo', 'bar.py')) == pjoin('renamed', 'foo', 'bar.py') + assert builder.config.sources[""] == pjoin("renamed", "") + assert builder.config.get_distribution_path("bar.py") == pjoin("renamed", "bar.py") + assert builder.config.get_distribution_path(pjoin("foo", "bar.py")) == pjoin("renamed", "foo", "bar.py") def test_global_mapping_path_empty_string(self, isolation): - config = {'tool': {'hatch': {'build': {'sources': {'src/foo': ''}}}}} + config = {"tool": {"hatch": {"build": {"sources": {"src/foo": ""}}}}} builder = MockBuilder(str(isolation), config=config) assert len(builder.config.sources) == 1 - assert builder.config.sources[pjoin('src', 'foo', '')] == '' - assert builder.config.get_distribution_path(pjoin('src', 'foo', 'bar.py')) == 'bar.py' + assert builder.config.sources[pjoin("src", "foo", "")] == "" + assert builder.config.get_distribution_path(pjoin("src", "foo", "bar.py")) == "bar.py" def test_global_mapping_replacement_not_string(self, isolation): - config = {'tool': {'hatch': {'build': {'sources': {'src/foo': 0}}}}} + config = {"tool": {"hatch": {"build": {"sources": {"src/foo": 0}}}}} builder = MockBuilder(str(isolation), config=config) with pytest.raises( - TypeError, match='Path for source `src/foo` in field `tool.hatch.build.sources` must be a string' + TypeError, match="Path for source `src/foo` in field `tool.hatch.build.sources` must be a string" ): _ = builder.config.sources def test_target_invalid_type(self, isolation): - config = {'tool': {'hatch': {'build': {'targets': {'foo': {'sources': ''}}}}}} + config = {"tool": {"hatch": {"build": {"targets": {"foo": {"sources": ""}}}}}} builder = MockBuilder(str(isolation), config=config) - builder.PLUGIN_NAME = 'foo' + builder.PLUGIN_NAME = "foo" with pytest.raises( - TypeError, match='Field `tool.hatch.build.targets.foo.sources` must be a mapping or array of strings' + TypeError, match="Field `tool.hatch.build.targets.foo.sources` must be a mapping or array of strings" ): _ = builder.config.sources def test_target_array(self, isolation): - config = {'tool': {'hatch': {'build': {'targets': {'foo': {'sources': ['src']}}}}}} + config = {"tool": {"hatch": {"build": {"targets": {"foo": {"sources": ["src"]}}}}}} builder = MockBuilder(str(isolation), config=config) - builder.PLUGIN_NAME = 'foo' + builder.PLUGIN_NAME = "foo" assert len(builder.config.sources) == 1 - assert builder.config.sources[pjoin('src', '')] == '' - assert builder.config.get_distribution_path(pjoin('src', 'foo', 'bar.py')) == pjoin('foo', 'bar.py') + assert builder.config.sources[pjoin("src", "")] == "" + assert builder.config.get_distribution_path(pjoin("src", "foo", "bar.py")) == pjoin("foo", "bar.py") def test_target_array_source_not_string(self, isolation): - config = {'tool': {'hatch': {'build': {'targets': {'foo': {'sources': [0]}}}}}} + config = {"tool": {"hatch": {"build": {"targets": {"foo": {"sources": [0]}}}}}} builder = MockBuilder(str(isolation), config=config) - builder.PLUGIN_NAME = 'foo' + builder.PLUGIN_NAME = "foo" with pytest.raises( - TypeError, match='Source #1 in field `tool.hatch.build.targets.foo.sources` must be a string' + TypeError, match="Source #1 in field `tool.hatch.build.targets.foo.sources` must be a string" ): _ = builder.config.sources def test_target_array_source_empty_string(self, isolation): - config = {'tool': {'hatch': {'build': {'targets': {'foo': {'sources': ['']}}}}}} + config = {"tool": {"hatch": {"build": {"targets": {"foo": {"sources": [""]}}}}}} builder = MockBuilder(str(isolation), config=config) - builder.PLUGIN_NAME = 'foo' + builder.PLUGIN_NAME = "foo" with pytest.raises( - ValueError, match='Source #1 in field `tool.hatch.build.targets.foo.sources` cannot be an empty string' + ValueError, match="Source #1 in field `tool.hatch.build.targets.foo.sources` cannot be an empty string" ): _ = builder.config.sources def test_target_mapping(self, isolation): - config = {'tool': {'hatch': {'build': {'targets': {'foo': {'sources': {'src/foo': 'renamed'}}}}}}} + config = {"tool": {"hatch": {"build": {"targets": {"foo": {"sources": {"src/foo": "renamed"}}}}}}} builder = MockBuilder(str(isolation), config=config) - builder.PLUGIN_NAME = 'foo' + builder.PLUGIN_NAME = "foo" assert len(builder.config.sources) == 1 - assert builder.config.sources[pjoin('src', 'foo', '')] == pjoin('renamed', '') - assert builder.config.get_distribution_path(pjoin('src', 'foo', 'bar.py')) == pjoin('renamed', 'bar.py') + assert builder.config.sources[pjoin("src", "foo", "")] == pjoin("renamed", "") + assert builder.config.get_distribution_path(pjoin("src", "foo", "bar.py")) == pjoin("renamed", "bar.py") def test_target_mapping_source_empty_string(self, isolation): - config = {'tool': {'hatch': {'build': {'targets': {'foo': {'sources': {'': 'renamed'}}}}}}} + config = {"tool": {"hatch": {"build": {"targets": {"foo": {"sources": {"": "renamed"}}}}}}} builder = MockBuilder(str(isolation), config=config) - builder.PLUGIN_NAME = 'foo' + builder.PLUGIN_NAME = "foo" assert len(builder.config.sources) == 1 - assert builder.config.sources[''] == pjoin('renamed', '') - assert builder.config.get_distribution_path(pjoin('bar.py')) == pjoin('renamed', 'bar.py') + assert builder.config.sources[""] == pjoin("renamed", "") + assert builder.config.get_distribution_path(pjoin("bar.py")) == pjoin("renamed", "bar.py") def test_target_mapping_path_empty_string(self, isolation): - config = {'tool': {'hatch': {'build': {'targets': {'foo': {'sources': {'src/foo': ''}}}}}}} + config = {"tool": {"hatch": {"build": {"targets": {"foo": {"sources": {"src/foo": ""}}}}}}} builder = MockBuilder(str(isolation), config=config) - builder.PLUGIN_NAME = 'foo' + builder.PLUGIN_NAME = "foo" assert len(builder.config.sources) == 1 - assert builder.config.sources[pjoin('src', 'foo', '')] == '' - assert builder.config.get_distribution_path(pjoin('src', 'foo', 'bar.py')) == 'bar.py' + assert builder.config.sources[pjoin("src", "foo", "")] == "" + assert builder.config.get_distribution_path(pjoin("src", "foo", "bar.py")) == "bar.py" def test_target_mapping_replacement_not_string(self, isolation): - config = {'tool': {'hatch': {'build': {'targets': {'foo': {'sources': {'src/foo': 0}}}}}}} + config = {"tool": {"hatch": {"build": {"targets": {"foo": {"sources": {"src/foo": 0}}}}}}} builder = MockBuilder(str(isolation), config=config) - builder.PLUGIN_NAME = 'foo' + builder.PLUGIN_NAME = "foo" with pytest.raises( TypeError, - match='Path for source `src/foo` in field `tool.hatch.build.targets.foo.sources` must be a string', + match="Path for source `src/foo` in field `tool.hatch.build.targets.foo.sources` must be a string", ): _ = builder.config.sources def test_target_overrides_global(self, isolation): - config = {'tool': {'hatch': {'build': {'sources': ['src'], 'targets': {'foo': {'sources': ['pkg']}}}}}} + config = {"tool": {"hatch": {"build": {"sources": ["src"], "targets": {"foo": {"sources": ["pkg"]}}}}}} builder = MockBuilder(str(isolation), config=config) - builder.PLUGIN_NAME = 'foo' + builder.PLUGIN_NAME = "foo" assert len(builder.config.sources) == 1 - assert builder.config.sources[pjoin('pkg', '')] == '' - assert builder.config.get_distribution_path(pjoin('pkg', 'foo', 'bar.py')) == pjoin('foo', 'bar.py') - assert builder.config.get_distribution_path(pjoin('src', 'foo', 'bar.py')) == pjoin('src', 'foo', 'bar.py') + assert builder.config.sources[pjoin("pkg", "")] == "" + assert builder.config.get_distribution_path(pjoin("pkg", "foo", "bar.py")) == pjoin("foo", "bar.py") + assert builder.config.get_distribution_path(pjoin("src", "foo", "bar.py")) == pjoin("src", "foo", "bar.py") def test_no_source(self, isolation): - config = {'tool': {'hatch': {'build': {'sources': ['bar']}}}} + config = {"tool": {"hatch": {"build": {"sources": ["bar"]}}}} builder = MockBuilder(str(isolation), config=config) assert len(builder.config.sources) == 1 - assert builder.config.sources[pjoin('bar', '')] == '' - assert builder.config.get_distribution_path(pjoin('foo', 'bar.py')) == pjoin('foo', 'bar.py') + assert builder.config.sources[pjoin("bar", "")] == "" + assert builder.config.get_distribution_path(pjoin("foo", "bar.py")) == pjoin("foo", "bar.py") def test_compatible_with_packages(self, isolation): - config = {'tool': {'hatch': {'build': {'sources': {'src/foo': 'renamed'}, 'packages': ['src/foo']}}}} + config = {"tool": {"hatch": {"build": {"sources": {"src/foo": "renamed"}, "packages": ["src/foo"]}}}} builder = MockBuilder(str(isolation), config=config) assert len(builder.config.sources) == 1 - assert builder.config.sources[pjoin('src', 'foo', '')] == pjoin('renamed', '') - assert builder.config.get_distribution_path(pjoin('src', 'foo', 'bar.py')) == pjoin('renamed', 'bar.py') + assert builder.config.sources[pjoin("src", "foo", "")] == pjoin("renamed", "") + assert builder.config.get_distribution_path(pjoin("src", "foo", "bar.py")) == pjoin("renamed", "bar.py") class TestForceInclude: @@ -817,134 +817,134 @@ def test_default(self, isolation): assert builder.config.force_include == builder.config.force_include == {} def test_global_invalid_type(self, isolation): - config = {'tool': {'hatch': {'build': {'force-include': ''}}}} + config = {"tool": {"hatch": {"build": {"force-include": ""}}}} builder = MockBuilder(str(isolation), config=config) - with pytest.raises(TypeError, match='Field `tool.hatch.build.force-include` must be a mapping'): + with pytest.raises(TypeError, match="Field `tool.hatch.build.force-include` must be a mapping"): _ = builder.config.force_include def test_global_absolute(self, isolation): - config = {'tool': {'hatch': {'build': {'force-include': {str(isolation / 'source'): '/target/'}}}}} + config = {"tool": {"hatch": {"build": {"force-include": {str(isolation / "source"): "/target/"}}}}} builder = MockBuilder(str(isolation), config=config) - assert builder.config.force_include == {str(isolation / 'source'): 'target'} + assert builder.config.force_include == {str(isolation / "source"): "target"} def test_global_relative(self, isolation): - config = {'tool': {'hatch': {'build': {'force-include': {'../source': '/target/'}}}}} - builder = MockBuilder(str(isolation / 'foo'), config=config) + config = {"tool": {"hatch": {"build": {"force-include": {"../source": "/target/"}}}}} + builder = MockBuilder(str(isolation / "foo"), config=config) - assert builder.config.force_include == {str(isolation / 'source'): 'target'} + assert builder.config.force_include == {str(isolation / "source"): "target"} def test_global_source_empty_string(self, isolation): - config = {'tool': {'hatch': {'build': {'force-include': {'': '/target/'}}}}} + config = {"tool": {"hatch": {"build": {"force-include": {"": "/target/"}}}}} builder = MockBuilder(str(isolation), config=config) with pytest.raises( - ValueError, match='Source #1 in field `tool.hatch.build.force-include` cannot be an empty string' + ValueError, match="Source #1 in field `tool.hatch.build.force-include` cannot be an empty string" ): _ = builder.config.force_include def test_global_relative_path_not_string(self, isolation): - config = {'tool': {'hatch': {'build': {'force-include': {'source': 0}}}}} + config = {"tool": {"hatch": {"build": {"force-include": {"source": 0}}}}} builder = MockBuilder(str(isolation), config=config) with pytest.raises( - TypeError, match='Path for source `source` in field `tool.hatch.build.force-include` must be a string' + TypeError, match="Path for source `source` in field `tool.hatch.build.force-include` must be a string" ): _ = builder.config.force_include def test_global_relative_path_empty_string(self, isolation): - config = {'tool': {'hatch': {'build': {'force-include': {'source': ''}}}}} + config = {"tool": {"hatch": {"build": {"force-include": {"source": ""}}}}} builder = MockBuilder(str(isolation), config=config) with pytest.raises( ValueError, - match='Path for source `source` in field `tool.hatch.build.force-include` cannot be an empty string', + match="Path for source `source` in field `tool.hatch.build.force-include` cannot be an empty string", ): _ = builder.config.force_include def test_target_invalid_type(self, isolation): - config = {'tool': {'hatch': {'build': {'targets': {'foo': {'force-include': ''}}}}}} + config = {"tool": {"hatch": {"build": {"targets": {"foo": {"force-include": ""}}}}}} builder = MockBuilder(str(isolation), config=config) - builder.PLUGIN_NAME = 'foo' + builder.PLUGIN_NAME = "foo" - with pytest.raises(TypeError, match='Field `tool.hatch.build.targets.foo.force-include` must be a mapping'): + with pytest.raises(TypeError, match="Field `tool.hatch.build.targets.foo.force-include` must be a mapping"): _ = builder.config.force_include def test_target_absolute(self, isolation): config = { - 'tool': { - 'hatch': {'build': {'targets': {'foo': {'force-include': {str(isolation / 'source'): '/target/'}}}}} + "tool": { + "hatch": {"build": {"targets": {"foo": {"force-include": {str(isolation / "source"): "/target/"}}}}} } } builder = MockBuilder(str(isolation), config=config) - builder.PLUGIN_NAME = 'foo' + builder.PLUGIN_NAME = "foo" - assert builder.config.force_include == {str(isolation / 'source'): 'target'} + assert builder.config.force_include == {str(isolation / "source"): "target"} def test_target_relative(self, isolation): - config = {'tool': {'hatch': {'build': {'targets': {'foo': {'force-include': {'../source': '/target/'}}}}}}} - builder = MockBuilder(str(isolation / 'foo'), config=config) - builder.PLUGIN_NAME = 'foo' + config = {"tool": {"hatch": {"build": {"targets": {"foo": {"force-include": {"../source": "/target/"}}}}}}} + builder = MockBuilder(str(isolation / "foo"), config=config) + builder.PLUGIN_NAME = "foo" - assert builder.config.force_include == {str(isolation / 'source'): 'target'} + assert builder.config.force_include == {str(isolation / "source"): "target"} def test_target_source_empty_string(self, isolation): - config = {'tool': {'hatch': {'build': {'targets': {'foo': {'force-include': {'': '/target/'}}}}}}} + config = {"tool": {"hatch": {"build": {"targets": {"foo": {"force-include": {"": "/target/"}}}}}}} builder = MockBuilder(str(isolation), config=config) - builder.PLUGIN_NAME = 'foo' + builder.PLUGIN_NAME = "foo" with pytest.raises( ValueError, - match='Source #1 in field `tool.hatch.build.targets.foo.force-include` cannot be an empty string', + match="Source #1 in field `tool.hatch.build.targets.foo.force-include` cannot be an empty string", ): _ = builder.config.force_include def test_target_relative_path_not_string(self, isolation): - config = {'tool': {'hatch': {'build': {'targets': {'foo': {'force-include': {'source': 0}}}}}}} + config = {"tool": {"hatch": {"build": {"targets": {"foo": {"force-include": {"source": 0}}}}}}} builder = MockBuilder(str(isolation), config=config) - builder.PLUGIN_NAME = 'foo' + builder.PLUGIN_NAME = "foo" with pytest.raises( TypeError, - match='Path for source `source` in field `tool.hatch.build.targets.foo.force-include` must be a string', + match="Path for source `source` in field `tool.hatch.build.targets.foo.force-include` must be a string", ): _ = builder.config.force_include def test_target_relative_path_empty_string(self, isolation): - config = {'tool': {'hatch': {'build': {'targets': {'foo': {'force-include': {'source': ''}}}}}}} + config = {"tool": {"hatch": {"build": {"targets": {"foo": {"force-include": {"source": ""}}}}}}} builder = MockBuilder(str(isolation), config=config) - builder.PLUGIN_NAME = 'foo' + builder.PLUGIN_NAME = "foo" with pytest.raises( ValueError, match=( - 'Path for source `source` in field `tool.hatch.build.targets.foo.force-include` ' - 'cannot be an empty string' + "Path for source `source` in field `tool.hatch.build.targets.foo.force-include` " + "cannot be an empty string" ), ): _ = builder.config.force_include def test_order(self, isolation): config = { - 'tool': { - 'hatch': { - 'build': { - 'force-include': { - '../very-nested': 'target1/embedded', - '../source1': '/target2/', - '../source2': '/target1/', + "tool": { + "hatch": { + "build": { + "force-include": { + "../very-nested": "target1/embedded", + "../source1": "/target2/", + "../source2": "/target1/", } } } } } - builder = MockBuilder(str(isolation / 'foo'), config=config) + builder = MockBuilder(str(isolation / "foo"), config=config) assert builder.config.force_include == { - str(isolation / 'source2'): 'target1', - str(isolation / 'very-nested'): f'target1{os.sep}embedded', - str(isolation / 'source1'): 'target2', + str(isolation / "source2"): "target1", + str(isolation / "very-nested"): f"target1{os.sep}embedded", + str(isolation / "source1"): "target2", } @@ -955,161 +955,161 @@ def test_default(self, isolation): assert builder.config.only_include == builder.config.only_include == {} def test_global_invalid_type(self, isolation): - config = {'tool': {'hatch': {'build': {'only-include': 9000}}}} + config = {"tool": {"hatch": {"build": {"only-include": 9000}}}} builder = MockBuilder(str(isolation), config=config) - with pytest.raises(TypeError, match='Field `tool.hatch.build.only-include` must be an array'): + with pytest.raises(TypeError, match="Field `tool.hatch.build.only-include` must be an array"): _ = builder.config.only_include def test_global_path_not_string(self, isolation): - config = {'tool': {'hatch': {'build': {'only-include': [9000]}}}} + config = {"tool": {"hatch": {"build": {"only-include": [9000]}}}} builder = MockBuilder(str(isolation), config=config) - with pytest.raises(TypeError, match='Path #1 in field `tool.hatch.build.only-include` must be a string'): + with pytest.raises(TypeError, match="Path #1 in field `tool.hatch.build.only-include` must be a string"): _ = builder.config.only_include - @pytest.mark.parametrize('path', ['/', '~/foo', '../foo']) + @pytest.mark.parametrize("path", ["/", "~/foo", "../foo"]) def test_global_not_relative(self, isolation, path): - config = {'tool': {'hatch': {'build': {'only-include': [path]}}}} + config = {"tool": {"hatch": {"build": {"only-include": [path]}}}} builder = MockBuilder(str(isolation), config=config) with pytest.raises( - ValueError, match=f'Path #1 in field `tool.hatch.build.only-include` must be relative: {path}' + ValueError, match=f"Path #1 in field `tool.hatch.build.only-include` must be relative: {path}" ): _ = builder.config.only_include def test_global_duplicate(self, isolation): - config = {'tool': {'hatch': {'build': {'only-include': ['/foo//bar', 'foo//bar/']}}}} + config = {"tool": {"hatch": {"build": {"only-include": ["/foo//bar", "foo//bar/"]}}}} builder = MockBuilder(str(isolation), config=config) with pytest.raises( - ValueError, match=re.escape(f'Duplicate path in field `tool.hatch.build.only-include`: foo{os.sep}bar') + ValueError, match=re.escape(f"Duplicate path in field `tool.hatch.build.only-include`: foo{os.sep}bar") ): _ = builder.config.only_include def test_global_correct(self, isolation): - config = {'tool': {'hatch': {'build': {'only-include': ['/foo//bar/']}}}} + config = {"tool": {"hatch": {"build": {"only-include": ["/foo//bar/"]}}}} builder = MockBuilder(str(isolation), config=config) - assert builder.config.only_include == {f'{isolation}{os.sep}foo{os.sep}bar': f'foo{os.sep}bar'} + assert builder.config.only_include == {f"{isolation}{os.sep}foo{os.sep}bar": f"foo{os.sep}bar"} def test_target_invalid_type(self, isolation): - config = {'tool': {'hatch': {'build': {'targets': {'foo': {'only-include': 9000}}}}}} + config = {"tool": {"hatch": {"build": {"targets": {"foo": {"only-include": 9000}}}}}} builder = MockBuilder(str(isolation), config=config) - builder.PLUGIN_NAME = 'foo' + builder.PLUGIN_NAME = "foo" - with pytest.raises(TypeError, match='Field `tool.hatch.build.targets.foo.only-include` must be an array'): + with pytest.raises(TypeError, match="Field `tool.hatch.build.targets.foo.only-include` must be an array"): _ = builder.config.only_include def test_target_path_not_string(self, isolation): - config = {'tool': {'hatch': {'build': {'targets': {'foo': {'only-include': [9000]}}}}}} + config = {"tool": {"hatch": {"build": {"targets": {"foo": {"only-include": [9000]}}}}}} builder = MockBuilder(str(isolation), config=config) - builder.PLUGIN_NAME = 'foo' + builder.PLUGIN_NAME = "foo" with pytest.raises( - TypeError, match='Path #1 in field `tool.hatch.build.targets.foo.only-include` must be a string' + TypeError, match="Path #1 in field `tool.hatch.build.targets.foo.only-include` must be a string" ): _ = builder.config.only_include - @pytest.mark.parametrize('path', ['/', '~/foo', '../foo']) + @pytest.mark.parametrize("path", ["/", "~/foo", "../foo"]) def test_target_not_relative(self, isolation, path): - config = {'tool': {'hatch': {'build': {'targets': {'foo': {'only-include': [path]}}}}}} + config = {"tool": {"hatch": {"build": {"targets": {"foo": {"only-include": [path]}}}}}} builder = MockBuilder(str(isolation), config=config) - builder.PLUGIN_NAME = 'foo' + builder.PLUGIN_NAME = "foo" with pytest.raises( - ValueError, match=f'Path #1 in field `tool.hatch.build.targets.foo.only-include` must be relative: {path}' + ValueError, match=f"Path #1 in field `tool.hatch.build.targets.foo.only-include` must be relative: {path}" ): _ = builder.config.only_include def test_target_duplicate(self, isolation): - config = {'tool': {'hatch': {'build': {'targets': {'foo': {'only-include': ['/foo//bar', 'foo//bar/']}}}}}} + config = {"tool": {"hatch": {"build": {"targets": {"foo": {"only-include": ["/foo//bar", "foo//bar/"]}}}}}} builder = MockBuilder(str(isolation), config=config) - builder.PLUGIN_NAME = 'foo' + builder.PLUGIN_NAME = "foo" with pytest.raises( ValueError, - match=re.escape(f'Duplicate path in field `tool.hatch.build.targets.foo.only-include`: foo{os.sep}bar'), + match=re.escape(f"Duplicate path in field `tool.hatch.build.targets.foo.only-include`: foo{os.sep}bar"), ): _ = builder.config.only_include def test_target_correct(self, isolation): - config = {'tool': {'hatch': {'build': {'targets': {'foo': {'only-include': ['/foo//bar/']}}}}}} + config = {"tool": {"hatch": {"build": {"targets": {"foo": {"only-include": ["/foo//bar/"]}}}}}} builder = MockBuilder(str(isolation), config=config) - builder.PLUGIN_NAME = 'foo' + builder.PLUGIN_NAME = "foo" - assert builder.config.only_include == {f'{isolation}{os.sep}foo{os.sep}bar': f'foo{os.sep}bar'} + assert builder.config.only_include == {f"{isolation}{os.sep}foo{os.sep}bar": f"foo{os.sep}bar"} class TestVersions: def test_default_known(self, isolation): builder = MockBuilder(str(isolation)) - builder.PLUGIN_NAME = 'foo' - builder.get_version_api = lambda: {'2': str, '1': str} + builder.PLUGIN_NAME = "foo" + builder.get_version_api = lambda: {"2": str, "1": str} - assert builder.config.versions == builder.config.versions == ['2', '1'] + assert builder.config.versions == builder.config.versions == ["2", "1"] def test_default_override(self, isolation): builder = MockBuilder(str(isolation)) - builder.PLUGIN_NAME = 'foo' - builder.get_default_versions = lambda: ['old', 'new', 'new'] + builder.PLUGIN_NAME = "foo" + builder.get_default_versions = lambda: ["old", "new", "new"] - assert builder.config.versions == builder.config.versions == ['old', 'new'] + assert builder.config.versions == builder.config.versions == ["old", "new"] def test_invalid_type(self, isolation): - config = {'tool': {'hatch': {'build': {'targets': {'foo': {'versions': ''}}}}}} + config = {"tool": {"hatch": {"build": {"targets": {"foo": {"versions": ""}}}}}} builder = MockBuilder(str(isolation), config=config) - builder.PLUGIN_NAME = 'foo' + builder.PLUGIN_NAME = "foo" with pytest.raises( - TypeError, match='Field `tool.hatch.build.targets.foo.versions` must be an array of strings' + TypeError, match="Field `tool.hatch.build.targets.foo.versions` must be an array of strings" ): _ = builder.config.versions def test_correct(self, isolation): - config = {'tool': {'hatch': {'build': {'targets': {'foo': {'versions': ['3.14', '1', '3.14']}}}}}} + config = {"tool": {"hatch": {"build": {"targets": {"foo": {"versions": ["3.14", "1", "3.14"]}}}}}} builder = MockBuilder(str(isolation), config=config) - builder.PLUGIN_NAME = 'foo' - builder.get_version_api = lambda: {'3.14': str, '42': str, '1': str} + builder.PLUGIN_NAME = "foo" + builder.get_version_api = lambda: {"3.14": str, "42": str, "1": str} - assert builder.config.versions == builder.config.versions == ['3.14', '1'] + assert builder.config.versions == builder.config.versions == ["3.14", "1"] def test_empty_default(self, isolation): - config = {'tool': {'hatch': {'build': {'targets': {'foo': {'versions': []}}}}}} + config = {"tool": {"hatch": {"build": {"targets": {"foo": {"versions": []}}}}}} builder = MockBuilder(str(isolation), config=config) - builder.PLUGIN_NAME = 'foo' - builder.get_default_versions = lambda: ['old', 'new'] + builder.PLUGIN_NAME = "foo" + builder.get_default_versions = lambda: ["old", "new"] - assert builder.config.versions == builder.config.versions == ['old', 'new'] + assert builder.config.versions == builder.config.versions == ["old", "new"] def test_version_not_string(self, isolation): - config = {'tool': {'hatch': {'build': {'targets': {'foo': {'versions': [1]}}}}}} + config = {"tool": {"hatch": {"build": {"targets": {"foo": {"versions": [1]}}}}}} builder = MockBuilder(str(isolation), config=config) - builder.PLUGIN_NAME = 'foo' + builder.PLUGIN_NAME = "foo" with pytest.raises( - TypeError, match='Version #1 in field `tool.hatch.build.targets.foo.versions` must be a string' + TypeError, match="Version #1 in field `tool.hatch.build.targets.foo.versions` must be a string" ): _ = builder.config.versions def test_version_empty_string(self, isolation): - config = {'tool': {'hatch': {'build': {'targets': {'foo': {'versions': ['']}}}}}} + config = {"tool": {"hatch": {"build": {"targets": {"foo": {"versions": [""]}}}}}} builder = MockBuilder(str(isolation), config=config) - builder.PLUGIN_NAME = 'foo' + builder.PLUGIN_NAME = "foo" with pytest.raises( - ValueError, match='Version #1 in field `tool.hatch.build.targets.foo.versions` cannot be an empty string' + ValueError, match="Version #1 in field `tool.hatch.build.targets.foo.versions` cannot be an empty string" ): _ = builder.config.versions def test_unknown_version(self, isolation): - config = {'tool': {'hatch': {'build': {'targets': {'foo': {'versions': ['9000', '1', '42']}}}}}} + config = {"tool": {"hatch": {"build": {"targets": {"foo": {"versions": ["9000", "1", "42"]}}}}}} builder = MockBuilder(str(isolation), config=config) - builder.PLUGIN_NAME = 'foo' - builder.get_version_api = lambda: {'1': str} + builder.PLUGIN_NAME = "foo" + builder.get_version_api = lambda: {"1": str} with pytest.raises( - ValueError, match='Unknown versions in field `tool.hatch.build.targets.foo.versions`: 42, 9000' + ValueError, match="Unknown versions in field `tool.hatch.build.targets.foo.versions`: 42, 9000" ): _ = builder.config.versions @@ -1121,83 +1121,83 @@ def test_default(self, isolation): assert builder.config.hook_config == builder.config.hook_config == {} def test_target_not_table(self, isolation): - config = {'tool': {'hatch': {'build': {'targets': {'foo': {'hooks': 'bar'}}}}}} + config = {"tool": {"hatch": {"build": {"targets": {"foo": {"hooks": "bar"}}}}}} builder = MockBuilder(str(isolation), config=config) - builder.PLUGIN_NAME = 'foo' + builder.PLUGIN_NAME = "foo" - with pytest.raises(TypeError, match='Field `tool.hatch.build.targets.foo.hooks` must be a table'): + with pytest.raises(TypeError, match="Field `tool.hatch.build.targets.foo.hooks` must be a table"): _ = builder.config.hook_config def test_target_hook_not_table(self, isolation): - config = {'tool': {'hatch': {'build': {'targets': {'foo': {'hooks': {'bar': 'baz'}}}}}}} + config = {"tool": {"hatch": {"build": {"targets": {"foo": {"hooks": {"bar": "baz"}}}}}}} builder = MockBuilder(str(isolation), config=config) - builder.PLUGIN_NAME = 'foo' + builder.PLUGIN_NAME = "foo" - with pytest.raises(TypeError, match='Field `tool.hatch.build.targets.foo.hooks.bar` must be a table'): + with pytest.raises(TypeError, match="Field `tool.hatch.build.targets.foo.hooks.bar` must be a table"): _ = builder.config.hook_config def test_global_not_table(self, isolation): - config = {'tool': {'hatch': {'build': {'hooks': 'foo'}}}} + config = {"tool": {"hatch": {"build": {"hooks": "foo"}}}} builder = MockBuilder(str(isolation), config=config) - with pytest.raises(TypeError, match='Field `tool.hatch.build.hooks` must be a table'): + with pytest.raises(TypeError, match="Field `tool.hatch.build.hooks` must be a table"): _ = builder.config.hook_config def test_global_hook_not_table(self, isolation): - config = {'tool': {'hatch': {'build': {'hooks': {'foo': 'bar'}}}}} + config = {"tool": {"hatch": {"build": {"hooks": {"foo": "bar"}}}}} builder = MockBuilder(str(isolation), config=config) - with pytest.raises(TypeError, match='Field `tool.hatch.build.hooks.foo` must be a table'): + with pytest.raises(TypeError, match="Field `tool.hatch.build.hooks.foo` must be a table"): _ = builder.config.hook_config def test_global(self, isolation): - config = {'tool': {'hatch': {'build': {'hooks': {'foo': {'bar': 'baz'}}}}}} + config = {"tool": {"hatch": {"build": {"hooks": {"foo": {"bar": "baz"}}}}}} builder = MockBuilder(str(isolation), config=config) - assert builder.config.hook_config['foo']['bar'] == 'baz' + assert builder.config.hook_config["foo"]["bar"] == "baz" def test_order(self, isolation): config = { - 'tool': { - 'hatch': { - 'build': {'hooks': {'foo': {'bar': 'baz'}}, 'targets': {'foo': {'hooks': {'baz': {'foo': 'bar'}}}}} + "tool": { + "hatch": { + "build": {"hooks": {"foo": {"bar": "baz"}}, "targets": {"foo": {"hooks": {"baz": {"foo": "bar"}}}}} } }, } builder = MockBuilder(str(isolation), config=config) - builder.PLUGIN_NAME = 'foo' + builder.PLUGIN_NAME = "foo" - assert builder.config.hook_config == {'foo': {'bar': 'baz'}, 'baz': {'foo': 'bar'}} + assert builder.config.hook_config == {"foo": {"bar": "baz"}, "baz": {"foo": "bar"}} def test_target_overrides_global(self, isolation): config = { - 'tool': { - 'hatch': { - 'build': {'hooks': {'foo': {'bar': 'baz'}}, 'targets': {'foo': {'hooks': {'foo': {'baz': 'bar'}}}}} + "tool": { + "hatch": { + "build": {"hooks": {"foo": {"bar": "baz"}}, "targets": {"foo": {"hooks": {"foo": {"baz": "bar"}}}}} } }, } builder = MockBuilder(str(isolation), config=config) - builder.PLUGIN_NAME = 'foo' + builder.PLUGIN_NAME = "foo" - assert builder.config.hook_config['foo']['baz'] == 'bar' + assert builder.config.hook_config["foo"]["baz"] == "bar" def test_env_var_no_hooks(self, isolation): - config = {'tool': {'hatch': {'build': {'hooks': {'foo': {'bar': 'baz'}}}}}} + config = {"tool": {"hatch": {"build": {"hooks": {"foo": {"bar": "baz"}}}}}} builder = MockBuilder(str(isolation), config=config) - with EnvVars({BuildEnvVars.NO_HOOKS: 'true'}): + with EnvVars({BuildEnvVars.NO_HOOKS: "true"}): assert builder.config.hook_config == {} def test_enable_by_default(self, isolation): config = { - 'tool': { - 'hatch': { - 'build': { - 'hooks': { - 'foo': {'bar': 'baz', 'enable-by-default': False}, - 'bar': {'foo': 'baz', 'enable-by-default': False}, - 'baz': {'foo': 'bar'}, + "tool": { + "hatch": { + "build": { + "hooks": { + "foo": {"bar": "baz", "enable-by-default": False}, + "bar": {"foo": "baz", "enable-by-default": False}, + "baz": {"foo": "bar"}, } } } @@ -1205,17 +1205,17 @@ def test_enable_by_default(self, isolation): } builder = MockBuilder(str(isolation), config=config) - assert builder.config.hook_config == {'baz': {'foo': 'bar'}} + assert builder.config.hook_config == {"baz": {"foo": "bar"}} def test_env_var_all_override_enable_by_default(self, isolation): config = { - 'tool': { - 'hatch': { - 'build': { - 'hooks': { - 'foo': {'bar': 'baz', 'enable-by-default': False}, - 'bar': {'foo': 'baz', 'enable-by-default': False}, - 'baz': {'foo': 'bar'}, + "tool": { + "hatch": { + "build": { + "hooks": { + "foo": {"bar": "baz", "enable-by-default": False}, + "bar": {"foo": "baz", "enable-by-default": False}, + "baz": {"foo": "bar"}, } } } @@ -1223,22 +1223,22 @@ def test_env_var_all_override_enable_by_default(self, isolation): } builder = MockBuilder(str(isolation), config=config) - with EnvVars({BuildEnvVars.HOOKS_ENABLE: 'true'}): + with EnvVars({BuildEnvVars.HOOKS_ENABLE: "true"}): assert builder.config.hook_config == { - 'foo': {'bar': 'baz', 'enable-by-default': False}, - 'bar': {'foo': 'baz', 'enable-by-default': False}, - 'baz': {'foo': 'bar'}, + "foo": {"bar": "baz", "enable-by-default": False}, + "bar": {"foo": "baz", "enable-by-default": False}, + "baz": {"foo": "bar"}, } def test_env_var_specific_override_enable_by_default(self, isolation): config = { - 'tool': { - 'hatch': { - 'build': { - 'hooks': { - 'foo': {'bar': 'baz', 'enable-by-default': False}, - 'bar': {'foo': 'baz', 'enable-by-default': False}, - 'baz': {'foo': 'bar'}, + "tool": { + "hatch": { + "build": { + "hooks": { + "foo": {"bar": "baz", "enable-by-default": False}, + "bar": {"foo": "baz", "enable-by-default": False}, + "baz": {"foo": "bar"}, } } } @@ -1246,10 +1246,10 @@ def test_env_var_specific_override_enable_by_default(self, isolation): } builder = MockBuilder(str(isolation), config=config) - with EnvVars({f'{BuildEnvVars.HOOK_ENABLE_PREFIX}FOO': 'true'}): + with EnvVars({f"{BuildEnvVars.HOOK_ENABLE_PREFIX}FOO": "true"}): assert builder.config.hook_config == { - 'foo': {'bar': 'baz', 'enable-by-default': False}, - 'baz': {'foo': 'bar'}, + "foo": {"bar": "baz", "enable-by-default": False}, + "baz": {"foo": "bar"}, } @@ -1260,330 +1260,330 @@ def test_default(self, isolation): assert builder.config.dependencies == builder.config.dependencies == [] def test_target_not_array(self, isolation): - config = {'tool': {'hatch': {'build': {'targets': {'foo': {'dependencies': 9000}}}}}} + config = {"tool": {"hatch": {"build": {"targets": {"foo": {"dependencies": 9000}}}}}} builder = MockBuilder(str(isolation), config=config) - builder.PLUGIN_NAME = 'foo' + builder.PLUGIN_NAME = "foo" - with pytest.raises(TypeError, match='Field `tool.hatch.build.targets.foo.dependencies` must be an array'): + with pytest.raises(TypeError, match="Field `tool.hatch.build.targets.foo.dependencies` must be an array"): _ = builder.config.dependencies def test_target_dependency_not_string(self, isolation): - config = {'tool': {'hatch': {'build': {'targets': {'foo': {'dependencies': [9000]}}}}}} + config = {"tool": {"hatch": {"build": {"targets": {"foo": {"dependencies": [9000]}}}}}} builder = MockBuilder(str(isolation), config=config) - builder.PLUGIN_NAME = 'foo' + builder.PLUGIN_NAME = "foo" with pytest.raises( - TypeError, match='Dependency #1 of field `tool.hatch.build.targets.foo.dependencies` must be a string' + TypeError, match="Dependency #1 of field `tool.hatch.build.targets.foo.dependencies` must be a string" ): _ = builder.config.dependencies def test_global_not_array(self, isolation): - config = {'tool': {'hatch': {'build': {'dependencies': 9000}}}} + config = {"tool": {"hatch": {"build": {"dependencies": 9000}}}} builder = MockBuilder(str(isolation), config=config) - builder.PLUGIN_NAME = 'foo' + builder.PLUGIN_NAME = "foo" - with pytest.raises(TypeError, match='Field `tool.hatch.build.dependencies` must be an array'): + with pytest.raises(TypeError, match="Field `tool.hatch.build.dependencies` must be an array"): _ = builder.config.dependencies def test_global_dependency_not_string(self, isolation): - config = {'tool': {'hatch': {'build': {'dependencies': [9000]}}}} + config = {"tool": {"hatch": {"build": {"dependencies": [9000]}}}} builder = MockBuilder(str(isolation), config=config) - builder.PLUGIN_NAME = 'foo' + builder.PLUGIN_NAME = "foo" - with pytest.raises(TypeError, match='Dependency #1 of field `tool.hatch.build.dependencies` must be a string'): + with pytest.raises(TypeError, match="Dependency #1 of field `tool.hatch.build.dependencies` must be a string"): _ = builder.config.dependencies def test_hook_require_runtime_dependencies_not_boolean(self, isolation): - config = {'tool': {'hatch': {'build': {'hooks': {'foo': {'require-runtime-dependencies': 9000}}}}}} + config = {"tool": {"hatch": {"build": {"hooks": {"foo": {"require-runtime-dependencies": 9000}}}}}} builder = MockBuilder(str(isolation), config=config) - builder.PLUGIN_NAME = 'foo' + builder.PLUGIN_NAME = "foo" with pytest.raises( - TypeError, match='Option `require-runtime-dependencies` of build hook `foo` must be a boolean' + TypeError, match="Option `require-runtime-dependencies` of build hook `foo` must be a boolean" ): _ = builder.config.dependencies def test_hook_require_runtime_features_not_array(self, isolation): - config = {'tool': {'hatch': {'build': {'hooks': {'foo': {'require-runtime-features': 9000}}}}}} + config = {"tool": {"hatch": {"build": {"hooks": {"foo": {"require-runtime-features": 9000}}}}}} builder = MockBuilder(str(isolation), config=config) - builder.PLUGIN_NAME = 'foo' + builder.PLUGIN_NAME = "foo" - with pytest.raises(TypeError, match='Option `require-runtime-features` of build hook `foo` must be an array'): + with pytest.raises(TypeError, match="Option `require-runtime-features` of build hook `foo` must be an array"): _ = builder.config.dependencies def test_hook_require_runtime_features_feature_not_string(self, isolation): - config = {'tool': {'hatch': {'build': {'hooks': {'foo': {'require-runtime-features': [9000]}}}}}} + config = {"tool": {"hatch": {"build": {"hooks": {"foo": {"require-runtime-features": [9000]}}}}}} builder = MockBuilder(str(isolation), config=config) - builder.PLUGIN_NAME = 'foo' + builder.PLUGIN_NAME = "foo" with pytest.raises( - TypeError, match='Feature #1 of option `require-runtime-features` of build hook `foo` must be a string' + TypeError, match="Feature #1 of option `require-runtime-features` of build hook `foo` must be a string" ): _ = builder.config.dependencies def test_hook_require_runtime_features_feature_empty_string(self, isolation): - config = {'tool': {'hatch': {'build': {'hooks': {'foo': {'require-runtime-features': ['']}}}}}} + config = {"tool": {"hatch": {"build": {"hooks": {"foo": {"require-runtime-features": [""]}}}}}} builder = MockBuilder(str(isolation), config=config) - builder.PLUGIN_NAME = 'foo' + builder.PLUGIN_NAME = "foo" with pytest.raises( ValueError, - match='Feature #1 of option `require-runtime-features` of build hook `foo` cannot be an empty string', + match="Feature #1 of option `require-runtime-features` of build hook `foo` cannot be an empty string", ): _ = builder.config.dependencies def test_hook_require_runtime_features_feature_unknown(self, isolation): config = { - 'project': {'name': 'my_app', 'version': '0.0.1'}, - 'tool': {'hatch': {'build': {'hooks': {'foo': {'require-runtime-features': ['foo_bar']}}}}}, + "project": {"name": "my_app", "version": "0.0.1"}, + "tool": {"hatch": {"build": {"hooks": {"foo": {"require-runtime-features": ["foo_bar"]}}}}}, } builder = MockBuilder(str(isolation), config=config) - builder.PLUGIN_NAME = 'foo' + builder.PLUGIN_NAME = "foo" with pytest.raises( ValueError, match=( - 'Feature `foo-bar` of option `require-runtime-features` of build hook `foo` is not defined in ' - 'field `project.optional-dependencies`' + "Feature `foo-bar` of option `require-runtime-features` of build hook `foo` is not defined in " + "field `project.optional-dependencies`" ), ): _ = builder.config.dependencies def test_hook_dependencies_not_array(self, isolation): - config = {'tool': {'hatch': {'build': {'hooks': {'foo': {'dependencies': 9000}}}}}} + config = {"tool": {"hatch": {"build": {"hooks": {"foo": {"dependencies": 9000}}}}}} builder = MockBuilder(str(isolation), config=config) - builder.PLUGIN_NAME = 'foo' + builder.PLUGIN_NAME = "foo" - with pytest.raises(TypeError, match='Option `dependencies` of build hook `foo` must be an array'): + with pytest.raises(TypeError, match="Option `dependencies` of build hook `foo` must be an array"): _ = builder.config.dependencies def test_hook_dependency_not_string(self, isolation): - config = {'tool': {'hatch': {'build': {'hooks': {'foo': {'dependencies': [9000]}}}}}} + config = {"tool": {"hatch": {"build": {"hooks": {"foo": {"dependencies": [9000]}}}}}} builder = MockBuilder(str(isolation), config=config) - builder.PLUGIN_NAME = 'foo' + builder.PLUGIN_NAME = "foo" with pytest.raises( - TypeError, match='Dependency #1 of option `dependencies` of build hook `foo` must be a string' + TypeError, match="Dependency #1 of option `dependencies` of build hook `foo` must be a string" ): _ = builder.config.dependencies def test_correct(self, isolation): config = { - 'tool': { - 'hatch': { - 'build': { - 'dependencies': ['bar'], - 'hooks': {'foobar': {'dependencies': ['test1']}}, - 'targets': {'foo': {'dependencies': ['baz'], 'hooks': {'foobar': {'dependencies': ['test2']}}}}, + "tool": { + "hatch": { + "build": { + "dependencies": ["bar"], + "hooks": {"foobar": {"dependencies": ["test1"]}}, + "targets": {"foo": {"dependencies": ["baz"], "hooks": {"foobar": {"dependencies": ["test2"]}}}}, } } } } builder = MockBuilder(str(isolation), config=config) - builder.PLUGIN_NAME = 'foo' + builder.PLUGIN_NAME = "foo" - assert builder.config.dependencies == ['baz', 'bar', 'test2'] + assert builder.config.dependencies == ["baz", "bar", "test2"] def test_require_runtime_dependencies(self, isolation): config = { - 'project': {'name': 'my-app', 'version': '0.0.1', 'dependencies': ['foo']}, - 'tool': { - 'hatch': { - 'build': { - 'require-runtime-dependencies': True, - 'dependencies': ['bar'], - 'hooks': {'foobar': {'dependencies': ['test1']}}, - 'targets': {'foo': {'dependencies': ['baz'], 'hooks': {'foobar': {'dependencies': ['test2']}}}}, + "project": {"name": "my-app", "version": "0.0.1", "dependencies": ["foo"]}, + "tool": { + "hatch": { + "build": { + "require-runtime-dependencies": True, + "dependencies": ["bar"], + "hooks": {"foobar": {"dependencies": ["test1"]}}, + "targets": {"foo": {"dependencies": ["baz"], "hooks": {"foobar": {"dependencies": ["test2"]}}}}, } } }, } builder = MockBuilder(str(isolation), config=config) - builder.PLUGIN_NAME = 'foo' + builder.PLUGIN_NAME = "foo" - assert builder.config.dependencies == ['baz', 'bar', 'test2', 'foo'] + assert builder.config.dependencies == ["baz", "bar", "test2", "foo"] def test_require_runtime_features(self, isolation): config = { - 'project': {'name': 'my-app', 'version': '0.0.1', 'optional-dependencies': {'bar_baz': ['foo']}}, - 'tool': { - 'hatch': { - 'build': { - 'require-runtime-features': ['bar-baz'], - 'dependencies': ['bar'], - 'hooks': {'foobar': {'dependencies': ['test1']}}, - 'targets': {'foo': {'dependencies': ['baz'], 'hooks': {'foobar': {'dependencies': ['test2']}}}}, + "project": {"name": "my-app", "version": "0.0.1", "optional-dependencies": {"bar_baz": ["foo"]}}, + "tool": { + "hatch": { + "build": { + "require-runtime-features": ["bar-baz"], + "dependencies": ["bar"], + "hooks": {"foobar": {"dependencies": ["test1"]}}, + "targets": {"foo": {"dependencies": ["baz"], "hooks": {"foobar": {"dependencies": ["test2"]}}}}, } } }, } builder = MockBuilder(str(isolation), config=config) - builder.PLUGIN_NAME = 'foo' + builder.PLUGIN_NAME = "foo" - assert builder.config.dependencies == ['baz', 'bar', 'test2', 'foo'] + assert builder.config.dependencies == ["baz", "bar", "test2", "foo"] def test_env_var_no_hooks(self, isolation): config = { - 'tool': { - 'hatch': { - 'build': { - 'hooks': { - 'foo': {'dependencies': ['foo']}, - 'bar': {'dependencies': ['bar']}, - 'baz': {'dependencies': ['baz']}, + "tool": { + "hatch": { + "build": { + "hooks": { + "foo": {"dependencies": ["foo"]}, + "bar": {"dependencies": ["bar"]}, + "baz": {"dependencies": ["baz"]}, }, } } } } builder = MockBuilder(str(isolation), config=config) - builder.PLUGIN_NAME = 'foo' + builder.PLUGIN_NAME = "foo" - with EnvVars({BuildEnvVars.NO_HOOKS: 'true'}): + with EnvVars({BuildEnvVars.NO_HOOKS: "true"}): assert builder.config.dependencies == [] def test_hooks_enable_by_default(self, isolation): config = { - 'tool': { - 'hatch': { - 'build': { - 'hooks': { - 'foo': {'dependencies': ['foo'], 'enable-by-default': False}, - 'bar': {'dependencies': ['bar'], 'enable-by-default': False}, - 'baz': {'dependencies': ['baz']}, + "tool": { + "hatch": { + "build": { + "hooks": { + "foo": {"dependencies": ["foo"], "enable-by-default": False}, + "bar": {"dependencies": ["bar"], "enable-by-default": False}, + "baz": {"dependencies": ["baz"]}, }, } } } } builder = MockBuilder(str(isolation), config=config) - builder.PLUGIN_NAME = 'foo' + builder.PLUGIN_NAME = "foo" - assert builder.config.dependencies == ['baz'] + assert builder.config.dependencies == ["baz"] def test_hooks_env_var_all_override_enable_by_default(self, isolation): config = { - 'tool': { - 'hatch': { - 'build': { - 'hooks': { - 'foo': {'dependencies': ['foo'], 'enable-by-default': False}, - 'bar': {'dependencies': ['bar'], 'enable-by-default': False}, - 'baz': {'dependencies': ['baz']}, + "tool": { + "hatch": { + "build": { + "hooks": { + "foo": {"dependencies": ["foo"], "enable-by-default": False}, + "bar": {"dependencies": ["bar"], "enable-by-default": False}, + "baz": {"dependencies": ["baz"]}, }, } } } } builder = MockBuilder(str(isolation), config=config) - builder.PLUGIN_NAME = 'foo' + builder.PLUGIN_NAME = "foo" - with EnvVars({BuildEnvVars.HOOKS_ENABLE: 'true'}): - assert builder.config.dependencies == ['foo', 'bar', 'baz'] + with EnvVars({BuildEnvVars.HOOKS_ENABLE: "true"}): + assert builder.config.dependencies == ["foo", "bar", "baz"] def test_hooks_env_var_specific_override_enable_by_default(self, isolation): config = { - 'tool': { - 'hatch': { - 'build': { - 'hooks': { - 'foo': {'dependencies': ['foo'], 'enable-by-default': False}, - 'bar': {'dependencies': ['bar'], 'enable-by-default': False}, - 'baz': {'dependencies': ['baz']}, + "tool": { + "hatch": { + "build": { + "hooks": { + "foo": {"dependencies": ["foo"], "enable-by-default": False}, + "bar": {"dependencies": ["bar"], "enable-by-default": False}, + "baz": {"dependencies": ["baz"]}, }, } } } } builder = MockBuilder(str(isolation), config=config) - builder.PLUGIN_NAME = 'foo' + builder.PLUGIN_NAME = "foo" - with EnvVars({f'{BuildEnvVars.HOOK_ENABLE_PREFIX}FOO': 'true'}): - assert builder.config.dependencies == ['foo', 'baz'] + with EnvVars({f"{BuildEnvVars.HOOK_ENABLE_PREFIX}FOO": "true"}): + assert builder.config.dependencies == ["foo", "baz"] def test_hooks_require_runtime_dependencies(self, isolation): config = { - 'project': {'name': 'my-app', 'version': '0.0.1', 'dependencies': ['baz']}, - 'tool': { - 'hatch': { - 'build': { - 'hooks': { - 'foo': {'dependencies': ['foo'], 'enable-by-default': False}, - 'bar': {'dependencies': ['bar'], 'require-runtime-dependencies': True}, + "project": {"name": "my-app", "version": "0.0.1", "dependencies": ["baz"]}, + "tool": { + "hatch": { + "build": { + "hooks": { + "foo": {"dependencies": ["foo"], "enable-by-default": False}, + "bar": {"dependencies": ["bar"], "require-runtime-dependencies": True}, }, } } }, } builder = MockBuilder(str(isolation), config=config) - builder.PLUGIN_NAME = 'foo' + builder.PLUGIN_NAME = "foo" - assert builder.config.dependencies == ['bar', 'baz'] + assert builder.config.dependencies == ["bar", "baz"] def test_hooks_require_runtime_dependencies_disabled(self, isolation): config = { - 'project': {'name': 'my-app', 'version': '0.0.1', 'dependencies': ['baz']}, - 'tool': { - 'hatch': { - 'build': { - 'hooks': { - 'foo': { - 'dependencies': ['foo'], - 'enable-by-default': False, - 'require-runtime-dependencies': True, + "project": {"name": "my-app", "version": "0.0.1", "dependencies": ["baz"]}, + "tool": { + "hatch": { + "build": { + "hooks": { + "foo": { + "dependencies": ["foo"], + "enable-by-default": False, + "require-runtime-dependencies": True, }, - 'bar': {'dependencies': ['bar']}, + "bar": {"dependencies": ["bar"]}, }, } } }, } builder = MockBuilder(str(isolation), config=config) - builder.PLUGIN_NAME = 'foo' + builder.PLUGIN_NAME = "foo" - assert builder.config.dependencies == ['bar'] + assert builder.config.dependencies == ["bar"] def test_hooks_require_runtime_features(self, isolation): config = { - 'project': {'name': 'my-app', 'version': '0.0.1', 'optional-dependencies': {'foo_bar': ['baz']}}, - 'tool': { - 'hatch': { - 'build': { - 'hooks': { - 'foo': {'dependencies': ['foo'], 'enable-by-default': False}, - 'bar': {'dependencies': ['bar'], 'require-runtime-features': ['foo-bar']}, + "project": {"name": "my-app", "version": "0.0.1", "optional-dependencies": {"foo_bar": ["baz"]}}, + "tool": { + "hatch": { + "build": { + "hooks": { + "foo": {"dependencies": ["foo"], "enable-by-default": False}, + "bar": {"dependencies": ["bar"], "require-runtime-features": ["foo-bar"]}, }, } } }, } builder = MockBuilder(str(isolation), config=config) - builder.PLUGIN_NAME = 'foo' + builder.PLUGIN_NAME = "foo" - assert builder.config.dependencies == ['bar', 'baz'] + assert builder.config.dependencies == ["bar", "baz"] def test_hooks_require_runtime_features_disabled(self, isolation): config = { - 'project': {'name': 'my-app', 'version': '0.0.1', 'optional-dependencies': {'foo_bar': ['baz']}}, - 'tool': { - 'hatch': { - 'build': { - 'hooks': { - 'foo': { - 'dependencies': ['foo'], - 'enable-by-default': False, - 'require-runtime-features': ['foo-bar'], + "project": {"name": "my-app", "version": "0.0.1", "optional-dependencies": {"foo_bar": ["baz"]}}, + "tool": { + "hatch": { + "build": { + "hooks": { + "foo": { + "dependencies": ["foo"], + "enable-by-default": False, + "require-runtime-features": ["foo-bar"], }, - 'bar': {'dependencies': ['bar']}, + "bar": {"dependencies": ["bar"]}, }, } } }, } builder = MockBuilder(str(isolation), config=config) - builder.PLUGIN_NAME = 'foo' + builder.PLUGIN_NAME = "foo" - assert builder.config.dependencies == ['bar'] + assert builder.config.dependencies == ["bar"] class TestFileSelectionDefaults: @@ -1610,7 +1610,7 @@ def test_only_include(self, isolation): def test_global_exclude(self, isolation): builder = MockBuilder(str(isolation)) - assert builder.config.default_global_exclude() == ['*.py[cdo]', '/dist'] + assert builder.config.default_global_exclude() == ["*.py[cdo]", "/dist"] class TestPatternInclude: @@ -1620,331 +1620,331 @@ def test_default(self, isolation): assert builder.config.include_spec is None def test_global_becomes_spec(self, isolation): - config = {'tool': {'hatch': {'build': {'include': ['foo']}}}} + config = {"tool": {"hatch": {"build": {"include": ["foo"]}}}} builder = MockBuilder(str(isolation), config=config) assert isinstance(builder.config.include_spec, pathspec.GitIgnoreSpec) def test_global_invalid_type(self, isolation): - config = {'tool': {'hatch': {'build': {'include': ''}}}} + config = {"tool": {"hatch": {"build": {"include": ""}}}} builder = MockBuilder(str(isolation), config=config) - with pytest.raises(TypeError, match='Field `tool.hatch.build.include` must be an array of strings'): + with pytest.raises(TypeError, match="Field `tool.hatch.build.include` must be an array of strings"): _ = builder.config.include_spec - @pytest.mark.parametrize('separator', ['/', '\\']) + @pytest.mark.parametrize("separator", ["/", "\\"]) def test_global(self, isolation, separator, platform): - if separator == '\\' and not platform.windows: - pytest.skip('Not running on Windows') + if separator == "\\" and not platform.windows: + pytest.skip("Not running on Windows") - config = {'tool': {'hatch': {'build': {'include': ['foo', 'bar/baz']}}}} + config = {"tool": {"hatch": {"build": {"include": ["foo", "bar/baz"]}}}} builder = MockBuilder(str(isolation), config=config) - assert builder.config.include_spec.match_file(f'foo{separator}file.py') - assert builder.config.include_spec.match_file(f'bar{separator}baz{separator}file.py') - assert not builder.config.include_spec.match_file(f'bar{separator}file.py') + assert builder.config.include_spec.match_file(f"foo{separator}file.py") + assert builder.config.include_spec.match_file(f"bar{separator}baz{separator}file.py") + assert not builder.config.include_spec.match_file(f"bar{separator}file.py") def test_global_pattern_not_string(self, isolation): - config = {'tool': {'hatch': {'build': {'include': [0]}}}} + config = {"tool": {"hatch": {"build": {"include": [0]}}}} builder = MockBuilder(str(isolation), config=config) - with pytest.raises(TypeError, match='Pattern #1 in field `tool.hatch.build.include` must be a string'): + with pytest.raises(TypeError, match="Pattern #1 in field `tool.hatch.build.include` must be a string"): _ = builder.config.include_spec def test_global_pattern_empty_string(self, isolation): - config = {'tool': {'hatch': {'build': {'include': ['']}}}} + config = {"tool": {"hatch": {"build": {"include": [""]}}}} builder = MockBuilder(str(isolation), config=config) with pytest.raises( - ValueError, match='Pattern #1 in field `tool.hatch.build.include` cannot be an empty string' + ValueError, match="Pattern #1 in field `tool.hatch.build.include` cannot be an empty string" ): _ = builder.config.include_spec - @pytest.mark.parametrize('separator', ['/', '\\']) + @pytest.mark.parametrize("separator", ["/", "\\"]) def test_global_packages_included(self, isolation, separator, platform): - if separator == '\\' and not platform.windows: - pytest.skip('Not running on Windows') + if separator == "\\" and not platform.windows: + pytest.skip("Not running on Windows") - config = {'tool': {'hatch': {'build': {'packages': ['bar'], 'include': ['foo']}}}} + config = {"tool": {"hatch": {"build": {"packages": ["bar"], "include": ["foo"]}}}} builder = MockBuilder(str(isolation), config=config) - assert builder.config.include_spec.match_file(f'foo{separator}file.py') - assert builder.config.include_spec.match_file(f'bar{separator}baz{separator}file.py') - assert not builder.config.include_spec.match_file(f'baz{separator}bar{separator}file.py') + assert builder.config.include_spec.match_file(f"foo{separator}file.py") + assert builder.config.include_spec.match_file(f"bar{separator}baz{separator}file.py") + assert not builder.config.include_spec.match_file(f"baz{separator}bar{separator}file.py") - @pytest.mark.parametrize('separator', ['/', '\\']) + @pytest.mark.parametrize("separator", ["/", "\\"]) def test_target(self, isolation, separator, platform): - if separator == '\\' and not platform.windows: - pytest.skip('Not running on Windows') + if separator == "\\" and not platform.windows: + pytest.skip("Not running on Windows") - config = {'tool': {'hatch': {'build': {'targets': {'foo': {'include': ['foo', 'bar/baz']}}}}}} + config = {"tool": {"hatch": {"build": {"targets": {"foo": {"include": ["foo", "bar/baz"]}}}}}} builder = MockBuilder(str(isolation), config=config) - builder.PLUGIN_NAME = 'foo' + builder.PLUGIN_NAME = "foo" - assert builder.config.include_spec.match_file(f'foo{separator}file.py') - assert builder.config.include_spec.match_file(f'bar{separator}baz{separator}file.py') - assert not builder.config.include_spec.match_file(f'bar{separator}file.py') + assert builder.config.include_spec.match_file(f"foo{separator}file.py") + assert builder.config.include_spec.match_file(f"bar{separator}baz{separator}file.py") + assert not builder.config.include_spec.match_file(f"bar{separator}file.py") def test_target_pattern_not_string(self, isolation): - config = {'tool': {'hatch': {'build': {'targets': {'foo': {'include': [0]}}}}}} + config = {"tool": {"hatch": {"build": {"targets": {"foo": {"include": [0]}}}}}} builder = MockBuilder(str(isolation), config=config) - builder.PLUGIN_NAME = 'foo' + builder.PLUGIN_NAME = "foo" with pytest.raises( - TypeError, match='Pattern #1 in field `tool.hatch.build.targets.foo.include` must be a string' + TypeError, match="Pattern #1 in field `tool.hatch.build.targets.foo.include` must be a string" ): _ = builder.config.include_spec def test_target_pattern_empty_string(self, isolation): - config = {'tool': {'hatch': {'build': {'targets': {'foo': {'include': ['']}}}}}} + config = {"tool": {"hatch": {"build": {"targets": {"foo": {"include": [""]}}}}}} builder = MockBuilder(str(isolation), config=config) - builder.PLUGIN_NAME = 'foo' + builder.PLUGIN_NAME = "foo" with pytest.raises( - ValueError, match='Pattern #1 in field `tool.hatch.build.targets.foo.include` cannot be an empty string' + ValueError, match="Pattern #1 in field `tool.hatch.build.targets.foo.include` cannot be an empty string" ): _ = builder.config.include_spec - @pytest.mark.parametrize('separator', ['/', '\\']) + @pytest.mark.parametrize("separator", ["/", "\\"]) def test_target_overrides_global(self, isolation, separator, platform): - if separator == '\\' and not platform.windows: - pytest.skip('Not running on Windows') + if separator == "\\" and not platform.windows: + pytest.skip("Not running on Windows") - config = {'tool': {'hatch': {'build': {'include': ['foo'], 'targets': {'foo': {'include': ['bar']}}}}}} + config = {"tool": {"hatch": {"build": {"include": ["foo"], "targets": {"foo": {"include": ["bar"]}}}}}} builder = MockBuilder(str(isolation), config=config) - builder.PLUGIN_NAME = 'foo' + builder.PLUGIN_NAME = "foo" - assert not builder.config.include_spec.match_file(f'foo{separator}file.py') - assert builder.config.include_spec.match_file(f'bar{separator}file.py') + assert not builder.config.include_spec.match_file(f"foo{separator}file.py") + assert builder.config.include_spec.match_file(f"bar{separator}file.py") - @pytest.mark.parametrize('separator', ['/', '\\']) + @pytest.mark.parametrize("separator", ["/", "\\"]) def test_target_packages_included(self, isolation, separator, platform): - if separator == '\\' and not platform.windows: - pytest.skip('Not running on Windows') + if separator == "\\" and not platform.windows: + pytest.skip("Not running on Windows") - config = {'tool': {'hatch': {'build': {'targets': {'foo': {'packages': ['bar'], 'include': ['foo']}}}}}} + config = {"tool": {"hatch": {"build": {"targets": {"foo": {"packages": ["bar"], "include": ["foo"]}}}}}} builder = MockBuilder(str(isolation), config=config) - builder.PLUGIN_NAME = 'foo' + builder.PLUGIN_NAME = "foo" - assert builder.config.include_spec.match_file(f'foo{separator}file.py') - assert builder.config.include_spec.match_file(f'bar{separator}baz{separator}file.py') - assert not builder.config.include_spec.match_file(f'baz{separator}bar{separator}file.py') + assert builder.config.include_spec.match_file(f"foo{separator}file.py") + assert builder.config.include_spec.match_file(f"bar{separator}baz{separator}file.py") + assert not builder.config.include_spec.match_file(f"baz{separator}bar{separator}file.py") class TestPatternExclude: - @pytest.mark.parametrize('separator', ['/', '\\']) + @pytest.mark.parametrize("separator", ["/", "\\"]) def test_default(self, isolation, separator, platform): - if separator == '\\' and not platform.windows: - pytest.skip('Not running on Windows') + if separator == "\\" and not platform.windows: + pytest.skip("Not running on Windows") builder = MockBuilder(str(isolation)) assert isinstance(builder.config.exclude_spec, pathspec.GitIgnoreSpec) - assert builder.config.exclude_spec.match_file(f'dist{separator}file.py') + assert builder.config.exclude_spec.match_file(f"dist{separator}file.py") def test_global_invalid_type(self, isolation): - config = {'tool': {'hatch': {'build': {'exclude': ''}}}} + config = {"tool": {"hatch": {"build": {"exclude": ""}}}} builder = MockBuilder(str(isolation), config=config) - with pytest.raises(TypeError, match='Field `tool.hatch.build.exclude` must be an array of strings'): + with pytest.raises(TypeError, match="Field `tool.hatch.build.exclude` must be an array of strings"): _ = builder.config.exclude_spec - @pytest.mark.parametrize('separator', ['/', '\\']) + @pytest.mark.parametrize("separator", ["/", "\\"]) def test_global(self, isolation, separator, platform): - if separator == '\\' and not platform.windows: - pytest.skip('Not running on Windows') + if separator == "\\" and not platform.windows: + pytest.skip("Not running on Windows") - config = {'tool': {'hatch': {'build': {'exclude': ['foo', 'bar/baz']}}}} + config = {"tool": {"hatch": {"build": {"exclude": ["foo", "bar/baz"]}}}} builder = MockBuilder(str(isolation), config=config) - assert builder.config.exclude_spec.match_file(f'foo{separator}file.py') - assert builder.config.exclude_spec.match_file(f'bar{separator}baz{separator}file.py') - assert not builder.config.exclude_spec.match_file(f'bar{separator}file.py') + assert builder.config.exclude_spec.match_file(f"foo{separator}file.py") + assert builder.config.exclude_spec.match_file(f"bar{separator}baz{separator}file.py") + assert not builder.config.exclude_spec.match_file(f"bar{separator}file.py") def test_global_pattern_not_string(self, isolation): - config = {'tool': {'hatch': {'build': {'exclude': [0]}}}} + config = {"tool": {"hatch": {"build": {"exclude": [0]}}}} builder = MockBuilder(str(isolation), config=config) - with pytest.raises(TypeError, match='Pattern #1 in field `tool.hatch.build.exclude` must be a string'): + with pytest.raises(TypeError, match="Pattern #1 in field `tool.hatch.build.exclude` must be a string"): _ = builder.config.exclude_spec def test_global_pattern_empty_string(self, isolation): - config = {'tool': {'hatch': {'build': {'exclude': ['']}}}} + config = {"tool": {"hatch": {"build": {"exclude": [""]}}}} builder = MockBuilder(str(isolation), config=config) with pytest.raises( - ValueError, match='Pattern #1 in field `tool.hatch.build.exclude` cannot be an empty string' + ValueError, match="Pattern #1 in field `tool.hatch.build.exclude` cannot be an empty string" ): _ = builder.config.exclude_spec - @pytest.mark.parametrize('separator', ['/', '\\']) + @pytest.mark.parametrize("separator", ["/", "\\"]) def test_target(self, isolation, separator, platform): - if separator == '\\' and not platform.windows: - pytest.skip('Not running on Windows') + if separator == "\\" and not platform.windows: + pytest.skip("Not running on Windows") - config = {'tool': {'hatch': {'build': {'targets': {'foo': {'exclude': ['foo', 'bar/baz']}}}}}} + config = {"tool": {"hatch": {"build": {"targets": {"foo": {"exclude": ["foo", "bar/baz"]}}}}}} builder = MockBuilder(str(isolation), config=config) - builder.PLUGIN_NAME = 'foo' + builder.PLUGIN_NAME = "foo" - assert builder.config.exclude_spec.match_file(f'foo{separator}file.py') - assert builder.config.exclude_spec.match_file(f'bar{separator}baz{separator}file.py') - assert not builder.config.exclude_spec.match_file(f'bar{separator}file.py') + assert builder.config.exclude_spec.match_file(f"foo{separator}file.py") + assert builder.config.exclude_spec.match_file(f"bar{separator}baz{separator}file.py") + assert not builder.config.exclude_spec.match_file(f"bar{separator}file.py") def test_target_pattern_not_string(self, isolation): - config = {'tool': {'hatch': {'build': {'targets': {'foo': {'exclude': [0]}}}}}} + config = {"tool": {"hatch": {"build": {"targets": {"foo": {"exclude": [0]}}}}}} builder = MockBuilder(str(isolation), config=config) - builder.PLUGIN_NAME = 'foo' + builder.PLUGIN_NAME = "foo" with pytest.raises( - TypeError, match='Pattern #1 in field `tool.hatch.build.targets.foo.exclude` must be a string' + TypeError, match="Pattern #1 in field `tool.hatch.build.targets.foo.exclude` must be a string" ): _ = builder.config.exclude_spec def test_target_pattern_empty_string(self, isolation): - config = {'tool': {'hatch': {'build': {'targets': {'foo': {'exclude': ['']}}}}}} + config = {"tool": {"hatch": {"build": {"targets": {"foo": {"exclude": [""]}}}}}} builder = MockBuilder(str(isolation), config=config) - builder.PLUGIN_NAME = 'foo' + builder.PLUGIN_NAME = "foo" with pytest.raises( - ValueError, match='Pattern #1 in field `tool.hatch.build.targets.foo.exclude` cannot be an empty string' + ValueError, match="Pattern #1 in field `tool.hatch.build.targets.foo.exclude` cannot be an empty string" ): _ = builder.config.exclude_spec - @pytest.mark.parametrize('separator', ['/', '\\']) + @pytest.mark.parametrize("separator", ["/", "\\"]) def test_target_overrides_global(self, isolation, separator, platform): - if separator == '\\' and not platform.windows: - pytest.skip('Not running on Windows') + if separator == "\\" and not platform.windows: + pytest.skip("Not running on Windows") - config = {'tool': {'hatch': {'build': {'exclude': ['foo'], 'targets': {'foo': {'exclude': ['bar']}}}}}} + config = {"tool": {"hatch": {"build": {"exclude": ["foo"], "targets": {"foo": {"exclude": ["bar"]}}}}}} builder = MockBuilder(str(isolation), config=config) - builder.PLUGIN_NAME = 'foo' + builder.PLUGIN_NAME = "foo" - assert not builder.config.exclude_spec.match_file(f'foo{separator}file.py') - assert builder.config.exclude_spec.match_file(f'bar{separator}file.py') + assert not builder.config.exclude_spec.match_file(f"foo{separator}file.py") + assert builder.config.exclude_spec.match_file(f"bar{separator}file.py") - @pytest.mark.parametrize('separator', ['/', '\\']) + @pytest.mark.parametrize("separator", ["/", "\\"]) def test_vcs_git(self, temp_dir, separator, platform): - if separator == '\\' and not platform.windows: - pytest.skip('Not running on Windows') + if separator == "\\" and not platform.windows: + pytest.skip("Not running on Windows") with temp_dir.as_cwd(): - config = {'tool': {'hatch': {'build': {'exclude': ['foo']}}}} + config = {"tool": {"hatch": {"build": {"exclude": ["foo"]}}}} builder = MockBuilder(str(temp_dir), config=config) - vcs_ignore_file = temp_dir / '.gitignore' - vcs_ignore_file.write_text('/bar\n*.pyc') + vcs_ignore_file = temp_dir / ".gitignore" + vcs_ignore_file.write_text("/bar\n*.pyc") - assert builder.config.exclude_spec.match_file(f'foo{separator}file.py') - assert builder.config.exclude_spec.match_file(f'bar{separator}file.py') - assert builder.config.exclude_spec.match_file(f'baz{separator}bar{separator}file.pyc') - assert not builder.config.exclude_spec.match_file(f'baz{separator}bar{separator}file.py') + assert builder.config.exclude_spec.match_file(f"foo{separator}file.py") + assert builder.config.exclude_spec.match_file(f"bar{separator}file.py") + assert builder.config.exclude_spec.match_file(f"baz{separator}bar{separator}file.pyc") + assert not builder.config.exclude_spec.match_file(f"baz{separator}bar{separator}file.py") - @pytest.mark.parametrize('separator', ['/', '\\']) + @pytest.mark.parametrize("separator", ["/", "\\"]) def test_ignore_vcs_git(self, temp_dir, separator, platform): - if separator == '\\' and not platform.windows: - pytest.skip('Not running on Windows') + if separator == "\\" and not platform.windows: + pytest.skip("Not running on Windows") with temp_dir.as_cwd(): - config = {'tool': {'hatch': {'build': {'ignore-vcs': True, 'exclude': ['foo']}}}} + config = {"tool": {"hatch": {"build": {"ignore-vcs": True, "exclude": ["foo"]}}}} builder = MockBuilder(str(temp_dir), config=config) - vcs_ignore_file = temp_dir / '.gitignore' - vcs_ignore_file.write_text('/bar\n*.pyc') + vcs_ignore_file = temp_dir / ".gitignore" + vcs_ignore_file.write_text("/bar\n*.pyc") - assert builder.config.exclude_spec.match_file(f'foo{separator}file.py') - assert not builder.config.exclude_spec.match_file(f'bar{separator}file.py') + assert builder.config.exclude_spec.match_file(f"foo{separator}file.py") + assert not builder.config.exclude_spec.match_file(f"bar{separator}file.py") - @pytest.mark.parametrize('separator', ['/', '\\']) + @pytest.mark.parametrize("separator", ["/", "\\"]) def test_vcs_git_boundary(self, temp_dir, separator, platform): - if separator == '\\' and not platform.windows: - pytest.skip('Not running on Windows') + if separator == "\\" and not platform.windows: + pytest.skip("Not running on Windows") - project_dir = temp_dir / 'project' + project_dir = temp_dir / "project" project_dir.mkdir() - (project_dir / '.git').mkdir() + (project_dir / ".git").mkdir() with project_dir.as_cwd(): - config = {'tool': {'hatch': {'build': {'exclude': ['foo']}}}} + config = {"tool": {"hatch": {"build": {"exclude": ["foo"]}}}} builder = MockBuilder(str(project_dir), config=config) - vcs_ignore_file = temp_dir / '.gitignore' - vcs_ignore_file.write_text('/bar\n*.pyc') + vcs_ignore_file = temp_dir / ".gitignore" + vcs_ignore_file.write_text("/bar\n*.pyc") - assert builder.config.exclude_spec.match_file(f'foo{separator}file.py') - assert not builder.config.exclude_spec.match_file(f'bar{separator}file.py') + assert builder.config.exclude_spec.match_file(f"foo{separator}file.py") + assert not builder.config.exclude_spec.match_file(f"bar{separator}file.py") - @pytest.mark.parametrize('separator', ['/', '\\']) + @pytest.mark.parametrize("separator", ["/", "\\"]) def test_vcs_git_exclude_whitelisted_file(self, temp_dir, separator, platform): - if separator == '\\' and not platform.windows: - pytest.skip('Not running on Windows') + if separator == "\\" and not platform.windows: + pytest.skip("Not running on Windows") with temp_dir.as_cwd(): - config = {'tool': {'hatch': {'build': {'exclude': ['foo/bar']}}}} + config = {"tool": {"hatch": {"build": {"exclude": ["foo/bar"]}}}} builder = MockBuilder(str(temp_dir), config=config) - vcs_ignore_file = temp_dir / '.gitignore' - vcs_ignore_file.write_text('foo/*\n!foo/bar') + vcs_ignore_file = temp_dir / ".gitignore" + vcs_ignore_file.write_text("foo/*\n!foo/bar") - assert builder.config.path_is_excluded(f'foo{separator}deb') is True - assert builder.config.path_is_excluded(f'foo{separator}bar') is True + assert builder.config.path_is_excluded(f"foo{separator}deb") is True + assert builder.config.path_is_excluded(f"foo{separator}bar") is True - @pytest.mark.parametrize('separator', ['/', '\\']) + @pytest.mark.parametrize("separator", ["/", "\\"]) def test_vcs_mercurial(self, temp_dir, separator, platform): - if separator == '\\' and not platform.windows: - pytest.skip('Not running on Windows') + if separator == "\\" and not platform.windows: + pytest.skip("Not running on Windows") with temp_dir.as_cwd(): - config = {'tool': {'hatch': {'build': {'exclude': ['foo']}}}} + config = {"tool": {"hatch": {"build": {"exclude": ["foo"]}}}} builder = MockBuilder(str(temp_dir), config=config) - vcs_ignore_file = temp_dir / '.hgignore' - vcs_ignore_file.write_text('syntax: glob\n/bar\n*.pyc') + vcs_ignore_file = temp_dir / ".hgignore" + vcs_ignore_file.write_text("syntax: glob\n/bar\n*.pyc") - assert builder.config.exclude_spec.match_file(f'foo{separator}file.py') - assert builder.config.exclude_spec.match_file(f'bar{separator}file.py') - assert builder.config.exclude_spec.match_file(f'baz{separator}bar{separator}file.pyc') - assert not builder.config.exclude_spec.match_file(f'baz{separator}bar{separator}file.py') + assert builder.config.exclude_spec.match_file(f"foo{separator}file.py") + assert builder.config.exclude_spec.match_file(f"bar{separator}file.py") + assert builder.config.exclude_spec.match_file(f"baz{separator}bar{separator}file.pyc") + assert not builder.config.exclude_spec.match_file(f"baz{separator}bar{separator}file.py") - @pytest.mark.parametrize('separator', ['/', '\\']) + @pytest.mark.parametrize("separator", ["/", "\\"]) def test_ignore_vcs_mercurial(self, temp_dir, separator, platform): - if separator == '\\' and not platform.windows: - pytest.skip('Not running on Windows') + if separator == "\\" and not platform.windows: + pytest.skip("Not running on Windows") with temp_dir.as_cwd(): - config = {'tool': {'hatch': {'build': {'ignore-vcs': True, 'exclude': ['foo']}}}} + config = {"tool": {"hatch": {"build": {"ignore-vcs": True, "exclude": ["foo"]}}}} builder = MockBuilder(str(temp_dir), config=config) - vcs_ignore_file = temp_dir / '.hgignore' - vcs_ignore_file.write_text('syntax: glob\n/bar\n*.pyc') + vcs_ignore_file = temp_dir / ".hgignore" + vcs_ignore_file.write_text("syntax: glob\n/bar\n*.pyc") - assert builder.config.exclude_spec.match_file(f'foo{separator}file.py') - assert not builder.config.exclude_spec.match_file(f'bar{separator}file.py') + assert builder.config.exclude_spec.match_file(f"foo{separator}file.py") + assert not builder.config.exclude_spec.match_file(f"bar{separator}file.py") - @pytest.mark.parametrize('separator', ['/', '\\']) + @pytest.mark.parametrize("separator", ["/", "\\"]) def test_vcs_mercurial_boundary(self, temp_dir, separator, platform): - if separator == '\\' and not platform.windows: - pytest.skip('Not running on Windows') + if separator == "\\" and not platform.windows: + pytest.skip("Not running on Windows") - project_dir = temp_dir / 'project' + project_dir = temp_dir / "project" project_dir.mkdir() - (project_dir / '.hg').mkdir() + (project_dir / ".hg").mkdir() with project_dir.as_cwd(): - config = {'tool': {'hatch': {'build': {'exclude': ['foo']}}}} + config = {"tool": {"hatch": {"build": {"exclude": ["foo"]}}}} builder = MockBuilder(str(project_dir), config=config) - vcs_ignore_file = temp_dir / '.hgignore' - vcs_ignore_file.write_text('syntax: glob\n/bar\n*.pyc') + vcs_ignore_file = temp_dir / ".hgignore" + vcs_ignore_file.write_text("syntax: glob\n/bar\n*.pyc") - assert builder.config.exclude_spec.match_file(f'foo{separator}file.py') - assert not builder.config.exclude_spec.match_file(f'bar{separator}file.py') + assert builder.config.exclude_spec.match_file(f"foo{separator}file.py") + assert not builder.config.exclude_spec.match_file(f"bar{separator}file.py") def test_override_default_global_exclude_patterns(self, isolation): builder = MockBuilder(str(isolation)) builder.config.default_global_exclude = list assert builder.config.exclude_spec is None - assert not builder.config.path_is_excluded('.git/file') + assert not builder.config.path_is_excluded(".git/file") class TestPatternArtifacts: @@ -1954,116 +1954,116 @@ def test_default(self, isolation): assert builder.config.artifact_spec is None def test_global_becomes_spec(self, isolation): - config = {'tool': {'hatch': {'build': {'artifacts': ['foo']}}}} + config = {"tool": {"hatch": {"build": {"artifacts": ["foo"]}}}} builder = MockBuilder(str(isolation), config=config) assert isinstance(builder.config.artifact_spec, pathspec.GitIgnoreSpec) def test_global_invalid_type(self, isolation): - config = {'tool': {'hatch': {'build': {'artifacts': ''}}}} + config = {"tool": {"hatch": {"build": {"artifacts": ""}}}} builder = MockBuilder(str(isolation), config=config) - with pytest.raises(TypeError, match='Field `tool.hatch.build.artifacts` must be an array of strings'): + with pytest.raises(TypeError, match="Field `tool.hatch.build.artifacts` must be an array of strings"): _ = builder.config.artifact_spec - @pytest.mark.parametrize('separator', ['/', '\\']) + @pytest.mark.parametrize("separator", ["/", "\\"]) def test_global(self, isolation, separator, platform): - if separator == '\\' and not platform.windows: - pytest.skip('Not running on Windows') + if separator == "\\" and not platform.windows: + pytest.skip("Not running on Windows") - config = {'tool': {'hatch': {'build': {'artifacts': ['foo', 'bar/baz']}}}} + config = {"tool": {"hatch": {"build": {"artifacts": ["foo", "bar/baz"]}}}} builder = MockBuilder(str(isolation), config=config) - assert builder.config.artifact_spec.match_file(f'foo{separator}file.py') - assert builder.config.artifact_spec.match_file(f'bar{separator}baz{separator}file.py') - assert not builder.config.artifact_spec.match_file(f'bar{separator}file.py') + assert builder.config.artifact_spec.match_file(f"foo{separator}file.py") + assert builder.config.artifact_spec.match_file(f"bar{separator}baz{separator}file.py") + assert not builder.config.artifact_spec.match_file(f"bar{separator}file.py") def test_global_pattern_not_string(self, isolation): - config = {'tool': {'hatch': {'build': {'artifacts': [0]}}}} + config = {"tool": {"hatch": {"build": {"artifacts": [0]}}}} builder = MockBuilder(str(isolation), config=config) - with pytest.raises(TypeError, match='Pattern #1 in field `tool.hatch.build.artifacts` must be a string'): + with pytest.raises(TypeError, match="Pattern #1 in field `tool.hatch.build.artifacts` must be a string"): _ = builder.config.artifact_spec def test_global_pattern_empty_string(self, isolation): - config = {'tool': {'hatch': {'build': {'artifacts': ['']}}}} + config = {"tool": {"hatch": {"build": {"artifacts": [""]}}}} builder = MockBuilder(str(isolation), config=config) with pytest.raises( - ValueError, match='Pattern #1 in field `tool.hatch.build.artifacts` cannot be an empty string' + ValueError, match="Pattern #1 in field `tool.hatch.build.artifacts` cannot be an empty string" ): _ = builder.config.artifact_spec - @pytest.mark.parametrize('separator', ['/', '\\']) + @pytest.mark.parametrize("separator", ["/", "\\"]) def test_target(self, isolation, separator, platform): - if separator == '\\' and not platform.windows: - pytest.skip('Not running on Windows') + if separator == "\\" and not platform.windows: + pytest.skip("Not running on Windows") - config = {'tool': {'hatch': {'build': {'targets': {'foo': {'artifacts': ['foo', 'bar/baz']}}}}}} + config = {"tool": {"hatch": {"build": {"targets": {"foo": {"artifacts": ["foo", "bar/baz"]}}}}}} builder = MockBuilder(str(isolation), config=config) - builder.PLUGIN_NAME = 'foo' + builder.PLUGIN_NAME = "foo" - assert builder.config.artifact_spec.match_file(f'foo{separator}file.py') - assert builder.config.artifact_spec.match_file(f'bar{separator}baz{separator}file.py') - assert not builder.config.artifact_spec.match_file(f'bar{separator}file.py') + assert builder.config.artifact_spec.match_file(f"foo{separator}file.py") + assert builder.config.artifact_spec.match_file(f"bar{separator}baz{separator}file.py") + assert not builder.config.artifact_spec.match_file(f"bar{separator}file.py") def test_target_pattern_not_string(self, isolation): - config = {'tool': {'hatch': {'build': {'targets': {'foo': {'artifacts': [0]}}}}}} + config = {"tool": {"hatch": {"build": {"targets": {"foo": {"artifacts": [0]}}}}}} builder = MockBuilder(str(isolation), config=config) - builder.PLUGIN_NAME = 'foo' + builder.PLUGIN_NAME = "foo" with pytest.raises( - TypeError, match='Pattern #1 in field `tool.hatch.build.targets.foo.artifacts` must be a string' + TypeError, match="Pattern #1 in field `tool.hatch.build.targets.foo.artifacts` must be a string" ): _ = builder.config.artifact_spec def test_target_pattern_empty_string(self, isolation): - config = {'tool': {'hatch': {'build': {'targets': {'foo': {'artifacts': ['']}}}}}} + config = {"tool": {"hatch": {"build": {"targets": {"foo": {"artifacts": [""]}}}}}} builder = MockBuilder(str(isolation), config=config) - builder.PLUGIN_NAME = 'foo' + builder.PLUGIN_NAME = "foo" with pytest.raises( - ValueError, match='Pattern #1 in field `tool.hatch.build.targets.foo.artifacts` cannot be an empty string' + ValueError, match="Pattern #1 in field `tool.hatch.build.targets.foo.artifacts` cannot be an empty string" ): _ = builder.config.artifact_spec - @pytest.mark.parametrize('separator', ['/', '\\']) + @pytest.mark.parametrize("separator", ["/", "\\"]) def test_target_overrides_global(self, isolation, separator, platform): - if separator == '\\' and not platform.windows: - pytest.skip('Not running on Windows') + if separator == "\\" and not platform.windows: + pytest.skip("Not running on Windows") - config = {'tool': {'hatch': {'build': {'artifacts': ['foo'], 'targets': {'foo': {'artifacts': ['bar']}}}}}} + config = {"tool": {"hatch": {"build": {"artifacts": ["foo"], "targets": {"foo": {"artifacts": ["bar"]}}}}}} builder = MockBuilder(str(isolation), config=config) - builder.PLUGIN_NAME = 'foo' + builder.PLUGIN_NAME = "foo" - assert not builder.config.artifact_spec.match_file(f'foo{separator}file.py') - assert builder.config.artifact_spec.match_file(f'bar{separator}file.py') + assert not builder.config.artifact_spec.match_file(f"foo{separator}file.py") + assert builder.config.artifact_spec.match_file(f"bar{separator}file.py") class TestPatternMatching: def test_include_explicit(self, isolation): - config = {'tool': {'hatch': {'build': {'include': ['foo']}}}} + config = {"tool": {"hatch": {"build": {"include": ["foo"]}}}} builder = MockBuilder(str(isolation), config=config) - assert builder.config.include_path('foo/file.py') - assert not builder.config.include_path('bar/file.py') + assert builder.config.include_path("foo/file.py") + assert not builder.config.include_path("bar/file.py") def test_no_include_greedy(self, isolation): builder = MockBuilder(str(isolation)) - assert builder.config.include_path('foo/file.py') - assert builder.config.include_path('bar/file.py') + assert builder.config.include_path("foo/file.py") + assert builder.config.include_path("bar/file.py") def test_exclude_precedence(self, isolation): - config = {'tool': {'hatch': {'build': {'include': ['foo'], 'exclude': ['foo']}}}} + config = {"tool": {"hatch": {"build": {"include": ["foo"], "exclude": ["foo"]}}}} builder = MockBuilder(str(isolation), config=config) - assert not builder.config.include_path('foo/file.py') - assert not builder.config.include_path('bar/file.py') + assert not builder.config.include_path("foo/file.py") + assert not builder.config.include_path("bar/file.py") def test_artifact_super_precedence(self, isolation): - config = {'tool': {'hatch': {'build': {'include': ['foo'], 'exclude': ['foo'], 'artifacts': ['foo']}}}} + config = {"tool": {"hatch": {"build": {"include": ["foo"], "exclude": ["foo"], "artifacts": ["foo"]}}}} builder = MockBuilder(str(isolation), config=config) - assert builder.config.include_path('foo/file.py') - assert not builder.config.include_path('bar/file.py') + assert builder.config.include_path("foo/file.py") + assert not builder.config.include_path("bar/file.py") diff --git a/tests/backend/builders/test_custom.py b/tests/backend/builders/test_custom.py index 02c43d43f..64e0df654 100644 --- a/tests/backend/builders/test_custom.py +++ b/tests/backend/builders/test_custom.py @@ -8,58 +8,58 @@ def test_target_config_not_table(isolation): - config = {'tool': {'hatch': {'build': {'targets': {'custom': 9000}}}}} + config = {"tool": {"hatch": {"build": {"targets": {"custom": 9000}}}}} - with pytest.raises(TypeError, match='Field `tool.hatch.build.targets.custom` must be a table'): + with pytest.raises(TypeError, match="Field `tool.hatch.build.targets.custom` must be a table"): CustomBuilder(str(isolation), config=config) def test_no_path(isolation): config = { - 'tool': { - 'hatch': { - 'build': {'targets': {'custom': {'path': ''}}}, + "tool": { + "hatch": { + "build": {"targets": {"custom": {"path": ""}}}, }, }, } - with pytest.raises(ValueError, match='Option `path` for builder `custom` must not be empty if defined'): + with pytest.raises(ValueError, match="Option `path` for builder `custom` must not be empty if defined"): CustomBuilder(str(isolation), config=config) def test_path_not_string(isolation): - config = {'tool': {'hatch': {'build': {'targets': {'custom': {'path': 3}}}}}} + config = {"tool": {"hatch": {"build": {"targets": {"custom": {"path": 3}}}}}} - with pytest.raises(TypeError, match='Option `path` for builder `custom` must be a string'): + with pytest.raises(TypeError, match="Option `path` for builder `custom` must be a string"): CustomBuilder(str(isolation), config=config) def test_nonexistent(isolation): - config = {'tool': {'hatch': {'build': {'targets': {'custom': {'path': 'test.py'}}}}}} + config = {"tool": {"hatch": {"build": {"targets": {"custom": {"path": "test.py"}}}}}} - with pytest.raises(OSError, match='Build script does not exist: test.py'): + with pytest.raises(OSError, match="Build script does not exist: test.py"): CustomBuilder(str(isolation), config=config) def test_default(hatch, helpers, temp_dir, config_file): - config_file.model.template.plugins['default']['src-layout'] = False + config_file.model.template.plugins["default"]["src-layout"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' + project_path = temp_dir / "my-app" config = { - 'project': {'name': project_name, 'dynamic': ['version']}, - 'tool': { - 'hatch': { - 'version': {'path': 'my_app/__about__.py'}, - 'build': {'targets': {'custom': {}}}, + "project": {"name": project_name, "dynamic": ["version"]}, + "tool": { + "hatch": { + "version": {"path": "my_app/__about__.py"}, + "build": {"targets": {"custom": {}}}, }, }, } @@ -87,7 +87,7 @@ def build(self, **kwargs): ) builder = CustomBuilder(str(project_path), config=config) - build_path = project_path / 'dist' + build_path = project_path / "dist" with project_path.as_cwd(): artifacts = list(builder.build()) @@ -98,51 +98,51 @@ def build(self, **kwargs): build_artifacts = list(build_path.iterdir()) assert len(build_artifacts) == 1 assert expected_artifact == str(build_artifacts[0]) - assert expected_artifact == str(build_path / 'custom-0.whl') + assert expected_artifact == str(build_path / "custom-0.whl") - extraction_directory = temp_dir / '_archive' + extraction_directory = temp_dir / "_archive" extraction_directory.mkdir() - with zipfile.ZipFile(str(expected_artifact), 'r') as zip_archive: + with zipfile.ZipFile(str(expected_artifact), "r") as zip_archive: zip_archive.extractall(str(extraction_directory)) - metadata_directory = f'{builder.project_id}.dist-info' + metadata_directory = f"{builder.project_id}.dist-info" expected_files = helpers.get_template_files( - 'wheel.standard_default_license_single', project_name, metadata_directory=metadata_directory + "wheel.standard_default_license_single", project_name, metadata_directory=metadata_directory ) helpers.assert_files(extraction_directory, expected_files) # Inspect the archive rather than the extracted files because on Windows they lose their metadata # https://stackoverflow.com/q/9813243 - with zipfile.ZipFile(str(expected_artifact), 'r') as zip_archive: - zip_info = zip_archive.getinfo(f'{metadata_directory}/WHEEL') + with zipfile.ZipFile(str(expected_artifact), "r") as zip_archive: + zip_info = zip_archive.getinfo(f"{metadata_directory}/WHEEL") assert zip_info.date_time == (2020, 2, 2, 0, 0, 0) def test_explicit_path(hatch, helpers, temp_dir, config_file): - config_file.model.template.plugins['default']['src-layout'] = False + config_file.model.template.plugins["default"]["src-layout"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' + project_path = temp_dir / "my-app" config = { - 'project': {'name': project_name, 'dynamic': ['version']}, - 'tool': { - 'hatch': { - 'version': {'path': 'my_app/__about__.py'}, - 'build': {'targets': {'custom': {'path': f'foo/{DEFAULT_BUILD_SCRIPT}'}}}, + "project": {"name": project_name, "dynamic": ["version"]}, + "tool": { + "hatch": { + "version": {"path": "my_app/__about__.py"}, + "build": {"targets": {"custom": {"path": f"foo/{DEFAULT_BUILD_SCRIPT}"}}}, }, }, } - file_path = project_path / 'foo' / DEFAULT_BUILD_SCRIPT + file_path = project_path / "foo" / DEFAULT_BUILD_SCRIPT file_path.ensure_parent_dir_exists() file_path.write_text( helpers.dedent( @@ -166,7 +166,7 @@ def build(self, **kwargs): ) builder = CustomBuilder(str(project_path), config=config) - build_path = project_path / 'dist' + build_path = project_path / "dist" with project_path.as_cwd(): artifacts = list(builder.build()) @@ -177,48 +177,48 @@ def build(self, **kwargs): build_artifacts = list(build_path.iterdir()) assert len(build_artifacts) == 1 assert expected_artifact == str(build_artifacts[0]) - assert expected_artifact == str(build_path / 'custom-0.whl') + assert expected_artifact == str(build_path / "custom-0.whl") - extraction_directory = temp_dir / '_archive' + extraction_directory = temp_dir / "_archive" extraction_directory.mkdir() - with zipfile.ZipFile(str(expected_artifact), 'r') as zip_archive: + with zipfile.ZipFile(str(expected_artifact), "r") as zip_archive: zip_archive.extractall(str(extraction_directory)) - metadata_directory = f'{builder.project_id}.dist-info' + metadata_directory = f"{builder.project_id}.dist-info" expected_files = helpers.get_template_files( - 'wheel.standard_default_license_single', project_name, metadata_directory=metadata_directory + "wheel.standard_default_license_single", project_name, metadata_directory=metadata_directory ) helpers.assert_files(extraction_directory, expected_files) # Inspect the archive rather than the extracted files because on Windows they lose their metadata # https://stackoverflow.com/q/9813243 - with zipfile.ZipFile(str(expected_artifact), 'r') as zip_archive: - zip_info = zip_archive.getinfo(f'{metadata_directory}/WHEEL') + with zipfile.ZipFile(str(expected_artifact), "r") as zip_archive: + zip_info = zip_archive.getinfo(f"{metadata_directory}/WHEEL") assert zip_info.date_time == (2020, 2, 2, 0, 0, 0) def test_no_subclass(hatch, helpers, temp_dir): - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' + project_path = temp_dir / "my-app" config = { - 'project': {'name': project_name, 'dynamic': ['version']}, - 'tool': { - 'hatch': { - 'version': {'path': 'my_app/__about__.py'}, - 'build': {'targets': {'custom': {'path': f'foo/{DEFAULT_BUILD_SCRIPT}'}}}, + "project": {"name": project_name, "dynamic": ["version"]}, + "tool": { + "hatch": { + "version": {"path": "my_app/__about__.py"}, + "build": {"targets": {"custom": {"path": f"foo/{DEFAULT_BUILD_SCRIPT}"}}}, }, }, } - file_path = project_path / 'foo' / DEFAULT_BUILD_SCRIPT + file_path = project_path / "foo" / DEFAULT_BUILD_SCRIPT file_path.ensure_parent_dir_exists() file_path.write_text( helpers.dedent( @@ -234,34 +234,39 @@ class CustomBuilder: ) ) - with pytest.raises( - ValueError, - match=re.escape(f'Unable to find a subclass of `BuilderInterface` in `foo/{DEFAULT_BUILD_SCRIPT}`: {temp_dir}'), - ), project_path.as_cwd(): + with ( + pytest.raises( + ValueError, + match=re.escape( + f"Unable to find a subclass of `BuilderInterface` in `foo/{DEFAULT_BUILD_SCRIPT}`: {temp_dir}" + ), + ), + project_path.as_cwd(), + ): CustomBuilder(str(project_path), config=config) def test_multiple_subclasses(hatch, helpers, temp_dir): - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' + project_path = temp_dir / "my-app" config = { - 'project': {'name': project_name, 'dynamic': ['version']}, - 'tool': { - 'hatch': { - 'version': {'path': 'my_app/__about__.py'}, - 'build': {'targets': {'custom': {'path': f'foo/{DEFAULT_BUILD_SCRIPT}'}}}, + "project": {"name": project_name, "dynamic": ["version"]}, + "tool": { + "hatch": { + "version": {"path": "my_app/__about__.py"}, + "build": {"targets": {"custom": {"path": f"foo/{DEFAULT_BUILD_SCRIPT}"}}}, }, }, } - file_path = project_path / 'foo' / DEFAULT_BUILD_SCRIPT + file_path = project_path / "foo" / DEFAULT_BUILD_SCRIPT file_path.ensure_parent_dir_exists() file_path.write_text( helpers.dedent( @@ -276,36 +281,39 @@ class CustomWheelBuilder(WheelBuilder): ) ) - with pytest.raises( - ValueError, - match=re.escape( - f'Multiple subclasses of `BuilderInterface` found in `foo/{DEFAULT_BUILD_SCRIPT}`, select ' - f'one by defining a function named `get_builder`: {temp_dir}' + with ( + pytest.raises( + ValueError, + match=re.escape( + f"Multiple subclasses of `BuilderInterface` found in `foo/{DEFAULT_BUILD_SCRIPT}`, select " + f"one by defining a function named `get_builder`: {temp_dir}" + ), ), - ), project_path.as_cwd(): + project_path.as_cwd(), + ): CustomBuilder(str(project_path), config=config) def test_dynamic_dependencies(hatch, helpers, temp_dir, config_file): - config_file.model.template.plugins['default']['src-layout'] = False + config_file.model.template.plugins["default"]["src-layout"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' + project_path = temp_dir / "my-app" config = { - 'project': {'name': project_name, 'dynamic': ['version']}, - 'tool': { - 'hatch': { - 'version': {'path': 'my_app/__about__.py'}, - 'build': { - 'targets': {'custom': {'dependencies': ['foo'], 'hooks': {'custom': {'dependencies': ['bar']}}}} + "project": {"name": project_name, "dynamic": ["version"]}, + "tool": { + "hatch": { + "version": {"path": "my_app/__about__.py"}, + "build": { + "targets": {"custom": {"dependencies": ["foo"], "hooks": {"custom": {"dependencies": ["bar"]}}}} }, }, }, @@ -332,4 +340,4 @@ def dependencies(self): ) builder = CustomBuilder(str(project_path), config=config) - assert builder.config.dependencies == ['foo', 'bar', 'baz'] + assert builder.config.dependencies == ["foo", "bar", "baz"] diff --git a/tests/backend/builders/test_sdist.py b/tests/backend/builders/test_sdist.py index 8d331445e..ddb24d059 100644 --- a/tests/backend/builders/test_sdist.py +++ b/tests/backend/builders/test_sdist.py @@ -17,7 +17,7 @@ def test_class(): def test_default_versions(isolation): builder = SdistBuilder(str(isolation)) - assert builder.get_default_versions() == ['standard'] + assert builder.get_default_versions() == ["standard"] class TestSupportLegacy: @@ -27,7 +27,7 @@ def test_default(self, isolation): assert builder.config.support_legacy is builder.config.support_legacy is False def test_target(self, isolation): - config = {'tool': {'hatch': {'build': {'targets': {'sdist': {'support-legacy': True}}}}}} + config = {"tool": {"hatch": {"build": {"targets": {"sdist": {"support-legacy": True}}}}}} builder = SdistBuilder(str(isolation), config=config) assert builder.config.support_legacy is builder.config.support_legacy is True @@ -41,23 +41,23 @@ def test_default(self, isolation): assert builder.config.core_metadata_constructor is get_core_metadata_constructors()[DEFAULT_METADATA_VERSION] def test_not_string(self, isolation): - config = {'tool': {'hatch': {'build': {'targets': {'sdist': {'core-metadata-version': 42}}}}}} + config = {"tool": {"hatch": {"build": {"targets": {"sdist": {"core-metadata-version": 42}}}}}} builder = SdistBuilder(str(isolation), config=config) with pytest.raises( - TypeError, match='Field `tool.hatch.build.targets.sdist.core-metadata-version` must be a string' + TypeError, match="Field `tool.hatch.build.targets.sdist.core-metadata-version` must be a string" ): _ = builder.config.core_metadata_constructor def test_unknown(self, isolation): - config = {'tool': {'hatch': {'build': {'targets': {'sdist': {'core-metadata-version': '9000'}}}}}} + config = {"tool": {"hatch": {"build": {"targets": {"sdist": {"core-metadata-version": "9000"}}}}}} builder = SdistBuilder(str(isolation), config=config) with pytest.raises( ValueError, match=( - f'Unknown metadata version `9000` for field `tool.hatch.build.targets.sdist.core-metadata-version`. ' - f'Available: {", ".join(sorted(get_core_metadata_constructors()))}' + f"Unknown metadata version `9000` for field `tool.hatch.build.targets.sdist.core-metadata-version`. " + f"Available: {', '.join(sorted(get_core_metadata_constructors()))}" ), ): _ = builder.config.core_metadata_constructor @@ -70,33 +70,33 @@ def test_default(self, isolation): assert builder.config.strict_naming is builder.config.strict_naming is True def test_target(self, isolation): - config = {'tool': {'hatch': {'build': {'targets': {'sdist': {'strict-naming': False}}}}}} + config = {"tool": {"hatch": {"build": {"targets": {"sdist": {"strict-naming": False}}}}}} builder = SdistBuilder(str(isolation), config=config) assert builder.config.strict_naming is False def test_target_not_boolean(self, isolation): - config = {'tool': {'hatch': {'build': {'targets': {'sdist': {'strict-naming': 9000}}}}}} + config = {"tool": {"hatch": {"build": {"targets": {"sdist": {"strict-naming": 9000}}}}}} builder = SdistBuilder(str(isolation), config=config) - with pytest.raises(TypeError, match='Field `tool.hatch.build.targets.sdist.strict-naming` must be a boolean'): + with pytest.raises(TypeError, match="Field `tool.hatch.build.targets.sdist.strict-naming` must be a boolean"): _ = builder.config.strict_naming def test_global(self, isolation): - config = {'tool': {'hatch': {'build': {'strict-naming': False}}}} + config = {"tool": {"hatch": {"build": {"strict-naming": False}}}} builder = SdistBuilder(str(isolation), config=config) assert builder.config.strict_naming is False def test_global_not_boolean(self, isolation): - config = {'tool': {'hatch': {'build': {'strict-naming': 9000}}}} + config = {"tool": {"hatch": {"build": {"strict-naming": 9000}}}} builder = SdistBuilder(str(isolation), config=config) - with pytest.raises(TypeError, match='Field `tool.hatch.build.strict-naming` must be a boolean'): + with pytest.raises(TypeError, match="Field `tool.hatch.build.strict-naming` must be a boolean"): _ = builder.config.strict_naming def test_target_overrides_global(self, isolation): - config = {'tool': {'hatch': {'build': {'strict-naming': False, 'targets': {'sdist': {'strict-naming': True}}}}}} + config = {"tool": {"hatch": {"build": {"strict-naming": False, "targets": {"sdist": {"strict-naming": True}}}}}} builder = SdistBuilder(str(isolation), config=config) assert builder.config.strict_naming is True @@ -104,7 +104,7 @@ def test_target_overrides_global(self, isolation): class TestConstructSetupPyFile: def test_default(self, helpers, isolation): - config = {'project': {'name': 'My.App', 'version': '0.1.0'}} + config = {"project": {"name": "My.App", "version": "0.1.0"}} builder = SdistBuilder(str(isolation), config=config) assert builder.construct_setup_py_file([]) == helpers.dedent( @@ -119,10 +119,10 @@ def test_default(self, helpers, isolation): ) def test_packages(self, helpers, isolation): - config = {'project': {'name': 'My.App', 'version': '0.1.0'}} + config = {"project": {"name": "My.App", "version": "0.1.0"}} builder = SdistBuilder(str(isolation), config=config) - assert builder.construct_setup_py_file(['my_app', os.path.join('my_app', 'pkg')]) == helpers.dedent( + assert builder.construct_setup_py_file(["my_app", os.path.join("my_app", "pkg")]) == helpers.dedent( """ from setuptools import setup @@ -138,10 +138,10 @@ def test_packages(self, helpers, isolation): ) def test_description(self, helpers, isolation): - config = {'project': {'name': 'My.App', 'version': '0.1.0', 'description': 'foo'}} + config = {"project": {"name": "My.App", "version": "0.1.0", "description": "foo"}} builder = SdistBuilder(str(isolation), config=config) - assert builder.construct_setup_py_file(['my_app', os.path.join('my_app', 'pkg')]) == helpers.dedent( + assert builder.construct_setup_py_file(["my_app", os.path.join("my_app", "pkg")]) == helpers.dedent( """ from setuptools import setup @@ -159,15 +159,15 @@ def test_description(self, helpers, isolation): def test_readme(self, helpers, isolation): config = { - 'project': { - 'name': 'My.App', - 'version': '0.1.0', - 'readme': {'content-type': 'text/markdown', 'text': 'test content\n'}, + "project": { + "name": "My.App", + "version": "0.1.0", + "readme": {"content-type": "text/markdown", "text": "test content\n"}, } } builder = SdistBuilder(str(isolation), config=config) - assert builder.construct_setup_py_file(['my_app', os.path.join('my_app', 'pkg')]) == helpers.dedent( + assert builder.construct_setup_py_file(["my_app", os.path.join("my_app", "pkg")]) == helpers.dedent( """ from setuptools import setup @@ -184,10 +184,10 @@ def test_readme(self, helpers, isolation): ) def test_authors_name(self, helpers, isolation): - config = {'project': {'name': 'My.App', 'version': '0.1.0', 'authors': [{'name': 'foo'}]}} + config = {"project": {"name": "My.App", "version": "0.1.0", "authors": [{"name": "foo"}]}} builder = SdistBuilder(str(isolation), config=config) - assert builder.construct_setup_py_file(['my_app', os.path.join('my_app', 'pkg')]) == helpers.dedent( + assert builder.construct_setup_py_file(["my_app", os.path.join("my_app", "pkg")]) == helpers.dedent( """ from setuptools import setup @@ -204,10 +204,10 @@ def test_authors_name(self, helpers, isolation): ) def test_authors_email(self, helpers, isolation): - config = {'project': {'name': 'My.App', 'version': '0.1.0', 'authors': [{'email': 'foo@domain'}]}} + config = {"project": {"name": "My.App", "version": "0.1.0", "authors": [{"email": "foo@domain"}]}} builder = SdistBuilder(str(isolation), config=config) - assert builder.construct_setup_py_file(['my_app', os.path.join('my_app', 'pkg')]) == helpers.dedent( + assert builder.construct_setup_py_file(["my_app", os.path.join("my_app", "pkg")]) == helpers.dedent( """ from setuptools import setup @@ -225,11 +225,11 @@ def test_authors_email(self, helpers, isolation): def test_authors_name_and_email(self, helpers, isolation): config = { - 'project': {'name': 'My.App', 'version': '0.1.0', 'authors': [{'email': 'bar@domain', 'name': 'foo'}]} + "project": {"name": "My.App", "version": "0.1.0", "authors": [{"email": "bar@domain", "name": "foo"}]} } builder = SdistBuilder(str(isolation), config=config) - assert builder.construct_setup_py_file(['my_app', os.path.join('my_app', 'pkg')]) == helpers.dedent( + assert builder.construct_setup_py_file(["my_app", os.path.join("my_app", "pkg")]) == helpers.dedent( """ from setuptools import setup @@ -246,10 +246,10 @@ def test_authors_name_and_email(self, helpers, isolation): ) def test_authors_multiple(self, helpers, isolation): - config = {'project': {'name': 'My.App', 'version': '0.1.0', 'authors': [{'name': 'foo'}, {'name': 'bar'}]}} + config = {"project": {"name": "My.App", "version": "0.1.0", "authors": [{"name": "foo"}, {"name": "bar"}]}} builder = SdistBuilder(str(isolation), config=config) - assert builder.construct_setup_py_file(['my_app', os.path.join('my_app', 'pkg')]) == helpers.dedent( + assert builder.construct_setup_py_file(["my_app", os.path.join("my_app", "pkg")]) == helpers.dedent( """ from setuptools import setup @@ -266,10 +266,10 @@ def test_authors_multiple(self, helpers, isolation): ) def test_maintainers_name(self, helpers, isolation): - config = {'project': {'name': 'My.App', 'version': '0.1.0', 'maintainers': [{'name': 'foo'}]}} + config = {"project": {"name": "My.App", "version": "0.1.0", "maintainers": [{"name": "foo"}]}} builder = SdistBuilder(str(isolation), config=config) - assert builder.construct_setup_py_file(['my_app', os.path.join('my_app', 'pkg')]) == helpers.dedent( + assert builder.construct_setup_py_file(["my_app", os.path.join("my_app", "pkg")]) == helpers.dedent( """ from setuptools import setup @@ -286,10 +286,10 @@ def test_maintainers_name(self, helpers, isolation): ) def test_maintainers_email(self, helpers, isolation): - config = {'project': {'name': 'My.App', 'version': '0.1.0', 'maintainers': [{'email': 'foo@domain'}]}} + config = {"project": {"name": "My.App", "version": "0.1.0", "maintainers": [{"email": "foo@domain"}]}} builder = SdistBuilder(str(isolation), config=config) - assert builder.construct_setup_py_file(['my_app', os.path.join('my_app', 'pkg')]) == helpers.dedent( + assert builder.construct_setup_py_file(["my_app", os.path.join("my_app", "pkg")]) == helpers.dedent( """ from setuptools import setup @@ -307,11 +307,11 @@ def test_maintainers_email(self, helpers, isolation): def test_maintainers_name_and_email(self, helpers, isolation): config = { - 'project': {'name': 'My.App', 'version': '0.1.0', 'maintainers': [{'email': 'bar@domain', 'name': 'foo'}]} + "project": {"name": "My.App", "version": "0.1.0", "maintainers": [{"email": "bar@domain", "name": "foo"}]} } builder = SdistBuilder(str(isolation), config=config) - assert builder.construct_setup_py_file(['my_app', os.path.join('my_app', 'pkg')]) == helpers.dedent( + assert builder.construct_setup_py_file(["my_app", os.path.join("my_app", "pkg")]) == helpers.dedent( """ from setuptools import setup @@ -328,10 +328,10 @@ def test_maintainers_name_and_email(self, helpers, isolation): ) def test_maintainers_multiple(self, helpers, isolation): - config = {'project': {'name': 'My.App', 'version': '0.1.0', 'maintainers': [{'name': 'foo'}, {'name': 'bar'}]}} + config = {"project": {"name": "My.App", "version": "0.1.0", "maintainers": [{"name": "foo"}, {"name": "bar"}]}} builder = SdistBuilder(str(isolation), config=config) - assert builder.construct_setup_py_file(['my_app', os.path.join('my_app', 'pkg')]) == helpers.dedent( + assert builder.construct_setup_py_file(["my_app", os.path.join("my_app", "pkg")]) == helpers.dedent( """ from setuptools import setup @@ -349,13 +349,13 @@ def test_maintainers_multiple(self, helpers, isolation): def test_classifiers(self, helpers, isolation): classifiers = [ - 'Programming Language :: Python :: 3.11', - 'Programming Language :: Python :: 3.9', + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.9", ] - config = {'project': {'name': 'My.App', 'version': '0.1.0', 'classifiers': classifiers}} + config = {"project": {"name": "My.App", "version": "0.1.0", "classifiers": classifiers}} builder = SdistBuilder(str(isolation), config=config) - assert builder.construct_setup_py_file(['my_app', os.path.join('my_app', 'pkg')]) == helpers.dedent( + assert builder.construct_setup_py_file(["my_app", os.path.join("my_app", "pkg")]) == helpers.dedent( """ from setuptools import setup @@ -375,10 +375,10 @@ def test_classifiers(self, helpers, isolation): ) def test_dependencies(self, helpers, isolation): - config = {'project': {'name': 'My.App', 'version': '0.1.0', 'dependencies': ['foo==1', 'bar==5']}} + config = {"project": {"name": "My.App", "version": "0.1.0", "dependencies": ["foo==1", "bar==5"]}} builder = SdistBuilder(str(isolation), config=config) - assert builder.construct_setup_py_file(['my_app', os.path.join('my_app', 'pkg')]) == helpers.dedent( + assert builder.construct_setup_py_file(["my_app", os.path.join("my_app", "pkg")]) == helpers.dedent( """ from setuptools import setup @@ -398,10 +398,10 @@ def test_dependencies(self, helpers, isolation): ) def test_dependencies_extra(self, helpers, isolation): - config = {'project': {'name': 'My.App', 'version': '0.1.0', 'dependencies': ['foo==1', 'bar==5']}} + config = {"project": {"name": "My.App", "version": "0.1.0", "dependencies": ["foo==1", "bar==5"]}} builder = SdistBuilder(str(isolation), config=config) - assert builder.construct_setup_py_file(['my_app', os.path.join('my_app', 'pkg')], ['baz==3']) == helpers.dedent( + assert builder.construct_setup_py_file(["my_app", os.path.join("my_app", "pkg")], ["baz==3"]) == helpers.dedent( """ from setuptools import setup @@ -423,18 +423,18 @@ def test_dependencies_extra(self, helpers, isolation): def test_optional_dependencies(self, helpers, isolation): config = { - 'project': { - 'name': 'My.App', - 'version': '0.1.0', - 'optional-dependencies': { - 'feature2': ['foo==1; python_version < "3"', 'bar==5'], - 'feature1': ['foo==1', 'bar==5; python_version < "3"'], + "project": { + "name": "My.App", + "version": "0.1.0", + "optional-dependencies": { + "feature2": ['foo==1; python_version < "3"', "bar==5"], + "feature1": ["foo==1", 'bar==5; python_version < "3"'], }, } } builder = SdistBuilder(str(isolation), config=config) - assert builder.construct_setup_py_file(['my_app', os.path.join('my_app', 'pkg')]) == helpers.dedent( + assert builder.construct_setup_py_file(["my_app", os.path.join("my_app", "pkg")]) == helpers.dedent( """ from setuptools import setup @@ -460,10 +460,10 @@ def test_optional_dependencies(self, helpers, isolation): ) def test_scripts(self, helpers, isolation): - config = {'project': {'name': 'My.App', 'version': '0.1.0', 'scripts': {'foo': 'pkg:bar', 'bar': 'pkg:foo'}}} + config = {"project": {"name": "My.App", "version": "0.1.0", "scripts": {"foo": "pkg:bar", "bar": "pkg:foo"}}} builder = SdistBuilder(str(isolation), config=config) - assert builder.construct_setup_py_file(['my_app', os.path.join('my_app', 'pkg')]) == helpers.dedent( + assert builder.construct_setup_py_file(["my_app", os.path.join("my_app", "pkg")]) == helpers.dedent( """ from setuptools import setup @@ -486,11 +486,11 @@ def test_scripts(self, helpers, isolation): def test_gui_scripts(self, helpers, isolation): config = { - 'project': {'name': 'My.App', 'version': '0.1.0', 'gui-scripts': {'foo': 'pkg:bar', 'bar': 'pkg:foo'}} + "project": {"name": "My.App", "version": "0.1.0", "gui-scripts": {"foo": "pkg:bar", "bar": "pkg:foo"}} } builder = SdistBuilder(str(isolation), config=config) - assert builder.construct_setup_py_file(['my_app', os.path.join('my_app', 'pkg')]) == helpers.dedent( + assert builder.construct_setup_py_file(["my_app", os.path.join("my_app", "pkg")]) == helpers.dedent( """ from setuptools import setup @@ -513,18 +513,18 @@ def test_gui_scripts(self, helpers, isolation): def test_entry_points(self, helpers, isolation): config = { - 'project': { - 'name': 'My.App', - 'version': '0.1.0', - 'entry-points': { - 'foo': {'bar': 'pkg:foo', 'foo': 'pkg:bar'}, - 'bar': {'foo': 'pkg:bar', 'bar': 'pkg:foo'}, + "project": { + "name": "My.App", + "version": "0.1.0", + "entry-points": { + "foo": {"bar": "pkg:foo", "foo": "pkg:bar"}, + "bar": {"foo": "pkg:bar", "bar": "pkg:foo"}, }, } } builder = SdistBuilder(str(isolation), config=config) - assert builder.construct_setup_py_file(['my_app', os.path.join('my_app', 'pkg')]) == helpers.dedent( + assert builder.construct_setup_py_file(["my_app", os.path.join("my_app", "pkg")]) == helpers.dedent( """ from setuptools import setup @@ -551,34 +551,34 @@ def test_entry_points(self, helpers, isolation): def test_all(self, helpers, isolation): config = { - 'project': { - 'name': 'My.App', - 'version': '0.1.0', - 'description': 'foo', - 'readme': {'content-type': 'text/markdown', 'text': 'test content\n'}, - 'authors': [{'email': 'bar@domain', 'name': 'foo'}], - 'maintainers': [{'email': 'bar@domain', 'name': 'foo'}], - 'classifiers': [ - 'Programming Language :: Python :: 3.11', - 'Programming Language :: Python :: 3.9', + "project": { + "name": "My.App", + "version": "0.1.0", + "description": "foo", + "readme": {"content-type": "text/markdown", "text": "test content\n"}, + "authors": [{"email": "bar@domain", "name": "foo"}], + "maintainers": [{"email": "bar@domain", "name": "foo"}], + "classifiers": [ + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.9", ], - 'dependencies': ['foo==1', 'bar==5'], - 'optional-dependencies': { - 'feature2': ['foo==1; python_version < "3"', 'bar==5'], - 'feature1': ['foo==1', 'bar==5; python_version < "3"'], - 'feature3': [], + "dependencies": ["foo==1", "bar==5"], + "optional-dependencies": { + "feature2": ['foo==1; python_version < "3"', "bar==5"], + "feature1": ["foo==1", 'bar==5; python_version < "3"'], + "feature3": [], }, - 'scripts': {'foo': 'pkg:bar', 'bar': 'pkg:foo'}, - 'gui-scripts': {'foo': 'pkg:bar', 'bar': 'pkg:foo'}, - 'entry-points': { - 'foo': {'bar': 'pkg:foo', 'foo': 'pkg:bar'}, - 'bar': {'foo': 'pkg:bar', 'bar': 'pkg:foo'}, + "scripts": {"foo": "pkg:bar", "bar": "pkg:foo"}, + "gui-scripts": {"foo": "pkg:bar", "bar": "pkg:foo"}, + "entry-points": { + "foo": {"bar": "pkg:foo", "foo": "pkg:bar"}, + "bar": {"foo": "pkg:bar", "bar": "pkg:foo"}, }, } } builder = SdistBuilder(str(isolation), config=config) - assert builder.construct_setup_py_file(['my_app', os.path.join('my_app', 'pkg')]) == helpers.dedent( + assert builder.construct_setup_py_file(["my_app", os.path.join("my_app", "pkg")]) == helpers.dedent( """ from setuptools import setup @@ -636,29 +636,29 @@ def test_all(self, helpers, isolation): class TestBuildStandard: def test_default(self, hatch, helpers, temp_dir, config_file): - config_file.model.template.plugins['default']['src-layout'] = False + config_file.model.template.plugins["default"]["src-layout"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' + project_path = temp_dir / "my-app" config = { - 'project': {'name': project_name, 'dynamic': ['version']}, - 'tool': { - 'hatch': { - 'version': {'path': 'my_app/__about__.py'}, - 'build': {'targets': {'sdist': {'versions': ['standard']}}}, + "project": {"name": project_name, "dynamic": ["version"]}, + "tool": { + "hatch": { + "version": {"path": "my_app/__about__.py"}, + "build": {"targets": {"sdist": {"versions": ["standard"]}}}, }, }, } builder = SdistBuilder(str(project_path), config=config) - build_path = project_path / 'dist' + build_path = project_path / "dist" with project_path.as_cwd(): artifacts = list(builder.build()) @@ -669,46 +669,46 @@ def test_default(self, hatch, helpers, temp_dir, config_file): build_artifacts = list(build_path.iterdir()) assert len(build_artifacts) == 1 assert expected_artifact == str(build_artifacts[0]) - assert expected_artifact == str(build_path / f'{builder.project_id}.tar.gz') + assert expected_artifact == str(build_path / f"{builder.project_id}.tar.gz") - extraction_directory = temp_dir / '_archive' + extraction_directory = temp_dir / "_archive" extraction_directory.mkdir() - with tarfile.open(str(expected_artifact), 'r:gz') as tar_archive: + with tarfile.open(str(expected_artifact), "r:gz") as tar_archive: tar_archive.extractall(str(extraction_directory), **helpers.tarfile_extraction_compat_options()) expected_files = helpers.get_template_files( - 'sdist.standard_default', project_name, relative_root=builder.project_id + "sdist.standard_default", project_name, relative_root=builder.project_id ) helpers.assert_files(extraction_directory, expected_files) - stat = os.stat(str(extraction_directory / builder.project_id / 'PKG-INFO')) + stat = os.stat(str(extraction_directory / builder.project_id / "PKG-INFO")) assert stat.st_mtime == get_reproducible_timestamp() def test_default_no_reproducible(self, hatch, helpers, temp_dir, config_file): - config_file.model.template.plugins['default']['src-layout'] = False + config_file.model.template.plugins["default"]["src-layout"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' + project_path = temp_dir / "my-app" config = { - 'project': {'name': project_name, 'dynamic': ['version']}, - 'tool': { - 'hatch': { - 'version': {'path': 'my_app/__about__.py'}, - 'build': {'targets': {'sdist': {'versions': ['standard'], 'reproducible': False}}}, + "project": {"name": project_name, "dynamic": ["version"]}, + "tool": { + "hatch": { + "version": {"path": "my_app/__about__.py"}, + "build": {"targets": {"sdist": {"versions": ["standard"], "reproducible": False}}}, }, }, } builder = SdistBuilder(str(project_path), config=config) - build_path = project_path / 'dist' + build_path = project_path / "dist" build_path.mkdir() with project_path.as_cwd(): @@ -720,46 +720,46 @@ def test_default_no_reproducible(self, hatch, helpers, temp_dir, config_file): build_artifacts = list(build_path.iterdir()) assert len(build_artifacts) == 1 assert expected_artifact == str(build_artifacts[0]) - assert expected_artifact == str(build_path / f'{builder.project_id}.tar.gz') + assert expected_artifact == str(build_path / f"{builder.project_id}.tar.gz") - extraction_directory = temp_dir / '_archive' + extraction_directory = temp_dir / "_archive" extraction_directory.mkdir() - with tarfile.open(str(expected_artifact), 'r:gz') as tar_archive: + with tarfile.open(str(expected_artifact), "r:gz") as tar_archive: tar_archive.extractall(str(extraction_directory), **helpers.tarfile_extraction_compat_options()) expected_files = helpers.get_template_files( - 'sdist.standard_default', project_name, relative_root=builder.project_id + "sdist.standard_default", project_name, relative_root=builder.project_id ) helpers.assert_files(extraction_directory, expected_files) - stat = os.stat(str(extraction_directory / builder.project_id / 'PKG-INFO')) + stat = os.stat(str(extraction_directory / builder.project_id / "PKG-INFO")) assert stat.st_mtime != get_reproducible_timestamp() def test_default_support_legacy(self, hatch, helpers, temp_dir, config_file): - config_file.model.template.plugins['default']['src-layout'] = False + config_file.model.template.plugins["default"]["src-layout"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' + project_path = temp_dir / "my-app" config = { - 'project': {'name': project_name, 'dynamic': ['version']}, - 'tool': { - 'hatch': { - 'version': {'path': 'my_app/__about__.py'}, - 'build': {'targets': {'sdist': {'versions': ['standard'], 'support-legacy': True}}}, + "project": {"name": project_name, "dynamic": ["version"]}, + "tool": { + "hatch": { + "version": {"path": "my_app/__about__.py"}, + "build": {"targets": {"sdist": {"versions": ["standard"], "support-legacy": True}}}, }, }, } builder = SdistBuilder(str(project_path), config=config) - build_path = project_path / 'dist' + build_path = project_path / "dist" build_path.mkdir() with project_path.as_cwd(): @@ -771,34 +771,34 @@ def test_default_support_legacy(self, hatch, helpers, temp_dir, config_file): build_artifacts = list(build_path.iterdir()) assert len(build_artifacts) == 1 assert expected_artifact == str(build_artifacts[0]) - assert expected_artifact == str(build_path / f'{builder.project_id}.tar.gz') + assert expected_artifact == str(build_path / f"{builder.project_id}.tar.gz") - extraction_directory = temp_dir / '_archive' + extraction_directory = temp_dir / "_archive" extraction_directory.mkdir() - with tarfile.open(str(expected_artifact), 'r:gz') as tar_archive: + with tarfile.open(str(expected_artifact), "r:gz") as tar_archive: tar_archive.extractall(str(extraction_directory), **helpers.tarfile_extraction_compat_options()) expected_files = helpers.get_template_files( - 'sdist.standard_default_support_legacy', project_name, relative_root=builder.project_id + "sdist.standard_default_support_legacy", project_name, relative_root=builder.project_id ) helpers.assert_files(extraction_directory, expected_files) def test_default_build_script_artifacts(self, hatch, helpers, temp_dir, config_file): - config_file.model.template.plugins['default']['src-layout'] = False + config_file.model.template.plugins["default"]["src-layout"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' + project_path = temp_dir / "my-app" - vcs_ignore_file = project_path / '.gitignore' - vcs_ignore_file.write_text('*.pyc\n*.so\n*.h\n') + vcs_ignore_file = project_path / ".gitignore" + vcs_ignore_file.write_text("*.pyc\n*.so\n*.h\n") build_script = project_path / DEFAULT_BUILD_SCRIPT build_script.write_text( @@ -817,23 +817,23 @@ def initialize(self, version, build_data): ) config = { - 'project': {'name': project_name, 'dynamic': ['version']}, - 'tool': { - 'hatch': { - 'version': {'path': 'my_app/__about__.py'}, - 'build': { - 'targets': { - 'sdist': {'versions': ['standard'], 'exclude': [DEFAULT_BUILD_SCRIPT, '.gitignore']} + "project": {"name": project_name, "dynamic": ["version"]}, + "tool": { + "hatch": { + "version": {"path": "my_app/__about__.py"}, + "build": { + "targets": { + "sdist": {"versions": ["standard"], "exclude": [DEFAULT_BUILD_SCRIPT, ".gitignore"]} }, - 'artifacts': ['my_app/lib.so'], - 'hooks': {'custom': {'path': DEFAULT_BUILD_SCRIPT}}, + "artifacts": ["my_app/lib.so"], + "hooks": {"custom": {"path": DEFAULT_BUILD_SCRIPT}}, }, }, }, } builder = SdistBuilder(str(project_path), config=config) - build_path = project_path / 'dist' + build_path = project_path / "dist" build_path.mkdir() with project_path.as_cwd(): @@ -845,34 +845,34 @@ def initialize(self, version, build_data): build_artifacts = list(build_path.iterdir()) assert len(build_artifacts) == 1 assert expected_artifact == str(build_artifacts[0]) - assert expected_artifact == str(build_path / f'{builder.project_id}.tar.gz') + assert expected_artifact == str(build_path / f"{builder.project_id}.tar.gz") - extraction_directory = temp_dir / '_archive' + extraction_directory = temp_dir / "_archive" extraction_directory.mkdir() - with tarfile.open(str(expected_artifact), 'r:gz') as tar_archive: + with tarfile.open(str(expected_artifact), "r:gz") as tar_archive: tar_archive.extractall(str(extraction_directory), **helpers.tarfile_extraction_compat_options()) expected_files = helpers.get_template_files( - 'sdist.standard_default_build_script_artifacts', project_name, relative_root=builder.project_id + "sdist.standard_default_build_script_artifacts", project_name, relative_root=builder.project_id ) helpers.assert_files(extraction_directory, expected_files) def test_default_build_script_extra_dependencies(self, hatch, helpers, temp_dir, config_file): - config_file.model.template.plugins['default']['src-layout'] = False + config_file.model.template.plugins["default"]["src-layout"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' + project_path = temp_dir / "my-app" - vcs_ignore_file = project_path / '.gitignore' - vcs_ignore_file.write_text('*.pyc\n*.so\n*.h\n') + vcs_ignore_file = project_path / ".gitignore" + vcs_ignore_file.write_text("*.pyc\n*.so\n*.h\n") build_script = project_path / DEFAULT_BUILD_SCRIPT build_script.write_text( @@ -892,23 +892,23 @@ def initialize(self, version, build_data): ) config = { - 'project': {'name': project_name, 'dynamic': ['version']}, - 'tool': { - 'hatch': { - 'version': {'path': 'my_app/__about__.py'}, - 'build': { - 'targets': { - 'sdist': {'versions': ['standard'], 'exclude': [DEFAULT_BUILD_SCRIPT, '.gitignore']} + "project": {"name": project_name, "dynamic": ["version"]}, + "tool": { + "hatch": { + "version": {"path": "my_app/__about__.py"}, + "build": { + "targets": { + "sdist": {"versions": ["standard"], "exclude": [DEFAULT_BUILD_SCRIPT, ".gitignore"]} }, - 'artifacts': ['my_app/lib.so'], - 'hooks': {'custom': {'path': DEFAULT_BUILD_SCRIPT}}, + "artifacts": ["my_app/lib.so"], + "hooks": {"custom": {"path": DEFAULT_BUILD_SCRIPT}}, }, }, }, } builder = SdistBuilder(str(project_path), config=config) - build_path = project_path / 'dist' + build_path = project_path / "dist" build_path.mkdir() with project_path.as_cwd(): @@ -920,45 +920,45 @@ def initialize(self, version, build_data): build_artifacts = list(build_path.iterdir()) assert len(build_artifacts) == 1 assert expected_artifact == str(build_artifacts[0]) - assert expected_artifact == str(build_path / f'{builder.project_id}.tar.gz') + assert expected_artifact == str(build_path / f"{builder.project_id}.tar.gz") - extraction_directory = temp_dir / '_archive' + extraction_directory = temp_dir / "_archive" extraction_directory.mkdir() - with tarfile.open(str(expected_artifact), 'r:gz') as tar_archive: + with tarfile.open(str(expected_artifact), "r:gz") as tar_archive: tar_archive.extractall(str(extraction_directory), **helpers.tarfile_extraction_compat_options()) expected_files = helpers.get_template_files( - 'sdist.standard_default_build_script_extra_dependencies', project_name, relative_root=builder.project_id + "sdist.standard_default_build_script_extra_dependencies", project_name, relative_root=builder.project_id ) helpers.assert_files(extraction_directory, expected_files) def test_include_project_file(self, hatch, helpers, temp_dir, config_file): - config_file.model.template.plugins['default']['src-layout'] = False + config_file.model.template.plugins["default"]["src-layout"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' + project_path = temp_dir / "my-app" config = { - 'project': {'name': project_name, 'dynamic': ['version'], 'readme': 'README.md'}, - 'tool': { - 'hatch': { - 'version': {'path': 'my_app/__about__.py'}, - 'build': { - 'targets': {'sdist': {'versions': ['standard'], 'include': ['my_app/', 'pyproject.toml']}} + "project": {"name": project_name, "dynamic": ["version"], "readme": "README.md"}, + "tool": { + "hatch": { + "version": {"path": "my_app/__about__.py"}, + "build": { + "targets": {"sdist": {"versions": ["standard"], "include": ["my_app/", "pyproject.toml"]}} }, }, }, } builder = SdistBuilder(str(project_path), config=config) - build_path = project_path / 'dist' + build_path = project_path / "dist" with project_path.as_cwd(): artifacts = list(builder.build()) @@ -969,45 +969,45 @@ def test_include_project_file(self, hatch, helpers, temp_dir, config_file): build_artifacts = list(build_path.iterdir()) assert len(build_artifacts) == 1 assert expected_artifact == str(build_artifacts[0]) - assert expected_artifact == str(build_path / f'{builder.project_id}.tar.gz') + assert expected_artifact == str(build_path / f"{builder.project_id}.tar.gz") - extraction_directory = temp_dir / '_archive' + extraction_directory = temp_dir / "_archive" extraction_directory.mkdir() - with tarfile.open(str(expected_artifact), 'r:gz') as tar_archive: + with tarfile.open(str(expected_artifact), "r:gz") as tar_archive: tar_archive.extractall(str(extraction_directory), **helpers.tarfile_extraction_compat_options()) expected_files = helpers.get_template_files( - 'sdist.standard_include', project_name, relative_root=builder.project_id + "sdist.standard_include", project_name, relative_root=builder.project_id ) helpers.assert_files(extraction_directory, expected_files) - stat = os.stat(str(extraction_directory / builder.project_id / 'PKG-INFO')) + stat = os.stat(str(extraction_directory / builder.project_id / "PKG-INFO")) assert stat.st_mtime == get_reproducible_timestamp() def test_project_file_always_included(self, hatch, helpers, temp_dir, config_file): - config_file.model.template.plugins['default']['src-layout'] = False + config_file.model.template.plugins["default"]["src-layout"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' + project_path = temp_dir / "my-app" config = { - 'project': {'name': project_name, 'dynamic': ['version'], 'readme': 'README.md'}, - 'tool': { - 'hatch': { - 'version': {'path': 'my_app/__about__.py'}, - 'build': { - 'targets': { - 'sdist': { - 'versions': ['standard'], - 'only-include': ['my_app'], - 'exclude': ['pyproject.toml'], + "project": {"name": project_name, "dynamic": ["version"], "readme": "README.md"}, + "tool": { + "hatch": { + "version": {"path": "my_app/__about__.py"}, + "build": { + "targets": { + "sdist": { + "versions": ["standard"], + "only-include": ["my_app"], + "exclude": ["pyproject.toml"], }, }, }, @@ -1017,9 +1017,9 @@ def test_project_file_always_included(self, hatch, helpers, temp_dir, config_fil builder = SdistBuilder(str(project_path), config=config) # Ensure that only the root project file is forcibly included - (project_path / 'my_app' / 'pyproject.toml').touch() + (project_path / "my_app" / "pyproject.toml").touch() - build_path = project_path / 'dist' + build_path = project_path / "dist" with project_path.as_cwd(): artifacts = list(builder.build()) @@ -1030,45 +1030,45 @@ def test_project_file_always_included(self, hatch, helpers, temp_dir, config_fil build_artifacts = list(build_path.iterdir()) assert len(build_artifacts) == 1 assert expected_artifact == str(build_artifacts[0]) - assert expected_artifact == str(build_path / f'{builder.project_id}.tar.gz') + assert expected_artifact == str(build_path / f"{builder.project_id}.tar.gz") - extraction_directory = temp_dir / '_archive' + extraction_directory = temp_dir / "_archive" extraction_directory.mkdir() - with tarfile.open(str(expected_artifact), 'r:gz') as tar_archive: + with tarfile.open(str(expected_artifact), "r:gz") as tar_archive: tar_archive.extractall(str(extraction_directory), **helpers.tarfile_extraction_compat_options()) expected_files = helpers.get_template_files( - 'sdist.standard_include', project_name, relative_root=builder.project_id + "sdist.standard_include", project_name, relative_root=builder.project_id ) helpers.assert_files(extraction_directory, expected_files) - stat = os.stat(str(extraction_directory / builder.project_id / 'PKG-INFO')) + stat = os.stat(str(extraction_directory / builder.project_id / "PKG-INFO")) assert stat.st_mtime == get_reproducible_timestamp() def test_config_file_always_included(self, hatch, helpers, temp_dir, config_file): - config_file.model.template.plugins['default']['src-layout'] = False + config_file.model.template.plugins["default"]["src-layout"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' + project_path = temp_dir / "my-app" config = { - 'project': {'name': project_name, 'dynamic': ['version'], 'readme': 'README.md'}, - 'tool': { - 'hatch': { - 'version': {'path': 'my_app/__about__.py'}, - 'build': { - 'targets': { - 'sdist': { - 'versions': ['standard'], - 'only-include': ['my_app'], - 'exclude': [DEFAULT_CONFIG_FILE], + "project": {"name": project_name, "dynamic": ["version"], "readme": "README.md"}, + "tool": { + "hatch": { + "version": {"path": "my_app/__about__.py"}, + "build": { + "targets": { + "sdist": { + "versions": ["standard"], + "only-include": ["my_app"], + "exclude": [DEFAULT_CONFIG_FILE], }, }, }, @@ -1080,9 +1080,9 @@ def test_config_file_always_included(self, hatch, helpers, temp_dir, config_file (project_path / DEFAULT_CONFIG_FILE).touch() # Ensure that only the root config file is forcibly included - (project_path / 'my_app' / DEFAULT_CONFIG_FILE).touch() + (project_path / "my_app" / DEFAULT_CONFIG_FILE).touch() - build_path = project_path / 'dist' + build_path = project_path / "dist" with project_path.as_cwd(): artifacts = list(builder.build()) @@ -1093,46 +1093,46 @@ def test_config_file_always_included(self, hatch, helpers, temp_dir, config_file build_artifacts = list(build_path.iterdir()) assert len(build_artifacts) == 1 assert expected_artifact == str(build_artifacts[0]) - assert expected_artifact == str(build_path / f'{builder.project_id}.tar.gz') + assert expected_artifact == str(build_path / f"{builder.project_id}.tar.gz") - extraction_directory = temp_dir / '_archive' + extraction_directory = temp_dir / "_archive" extraction_directory.mkdir() - with tarfile.open(str(expected_artifact), 'r:gz') as tar_archive: + with tarfile.open(str(expected_artifact), "r:gz") as tar_archive: tar_archive.extractall(str(extraction_directory), **helpers.tarfile_extraction_compat_options()) expected_files = helpers.get_template_files( - 'sdist.standard_include_config_file', project_name, relative_root=builder.project_id + "sdist.standard_include_config_file", project_name, relative_root=builder.project_id ) helpers.assert_files(extraction_directory, expected_files) - stat = os.stat(str(extraction_directory / builder.project_id / 'PKG-INFO')) + stat = os.stat(str(extraction_directory / builder.project_id / "PKG-INFO")) assert stat.st_mtime == get_reproducible_timestamp() def test_include_readme(self, hatch, helpers, temp_dir, config_file): - config_file.model.template.plugins['default']['src-layout'] = False + config_file.model.template.plugins["default"]["src-layout"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' + project_path = temp_dir / "my-app" config = { - 'project': {'name': project_name, 'dynamic': ['version'], 'readme': 'README.md'}, - 'tool': { - 'hatch': { - 'version': {'path': 'my_app/__about__.py'}, - 'build': {'targets': {'sdist': {'versions': ['standard'], 'include': ['my_app/', 'README.md']}}}, + "project": {"name": project_name, "dynamic": ["version"], "readme": "README.md"}, + "tool": { + "hatch": { + "version": {"path": "my_app/__about__.py"}, + "build": {"targets": {"sdist": {"versions": ["standard"], "include": ["my_app/", "README.md"]}}}, }, }, } builder = SdistBuilder(str(project_path), config=config) - build_path = project_path / 'dist' + build_path = project_path / "dist" with project_path.as_cwd(): artifacts = list(builder.build()) @@ -1143,42 +1143,42 @@ def test_include_readme(self, hatch, helpers, temp_dir, config_file): build_artifacts = list(build_path.iterdir()) assert len(build_artifacts) == 1 assert expected_artifact == str(build_artifacts[0]) - assert expected_artifact == str(build_path / f'{builder.project_id}.tar.gz') + assert expected_artifact == str(build_path / f"{builder.project_id}.tar.gz") - extraction_directory = temp_dir / '_archive' + extraction_directory = temp_dir / "_archive" extraction_directory.mkdir() - with tarfile.open(str(expected_artifact), 'r:gz') as tar_archive: + with tarfile.open(str(expected_artifact), "r:gz") as tar_archive: tar_archive.extractall(str(extraction_directory), **helpers.tarfile_extraction_compat_options()) expected_files = helpers.get_template_files( - 'sdist.standard_include', project_name, relative_root=builder.project_id + "sdist.standard_include", project_name, relative_root=builder.project_id ) helpers.assert_files(extraction_directory, expected_files) - stat = os.stat(str(extraction_directory / builder.project_id / 'PKG-INFO')) + stat = os.stat(str(extraction_directory / builder.project_id / "PKG-INFO")) assert stat.st_mtime == get_reproducible_timestamp() def test_readme_always_included(self, hatch, helpers, temp_dir, config_file): - config_file.model.template.plugins['default']['src-layout'] = False + config_file.model.template.plugins["default"]["src-layout"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' + project_path = temp_dir / "my-app" config = { - 'project': {'name': project_name, 'dynamic': ['version'], 'readme': 'README.md'}, - 'tool': { - 'hatch': { - 'version': {'path': 'my_app/__about__.py'}, - 'build': { - 'targets': { - 'sdist': {'versions': ['standard'], 'only-include': ['my_app'], 'exclude': ['README.md']}, + "project": {"name": project_name, "dynamic": ["version"], "readme": "README.md"}, + "tool": { + "hatch": { + "version": {"path": "my_app/__about__.py"}, + "build": { + "targets": { + "sdist": {"versions": ["standard"], "only-include": ["my_app"], "exclude": ["README.md"]}, }, }, }, @@ -1187,9 +1187,9 @@ def test_readme_always_included(self, hatch, helpers, temp_dir, config_file): builder = SdistBuilder(str(project_path), config=config) # Ensure that only the desired readme is forcibly included - (project_path / 'my_app' / 'README.md').touch() + (project_path / "my_app" / "README.md").touch() - build_path = project_path / 'dist' + build_path = project_path / "dist" with project_path.as_cwd(): artifacts = list(builder.build()) @@ -1200,46 +1200,46 @@ def test_readme_always_included(self, hatch, helpers, temp_dir, config_file): build_artifacts = list(build_path.iterdir()) assert len(build_artifacts) == 1 assert expected_artifact == str(build_artifacts[0]) - assert expected_artifact == str(build_path / f'{builder.project_id}.tar.gz') + assert expected_artifact == str(build_path / f"{builder.project_id}.tar.gz") - extraction_directory = temp_dir / '_archive' + extraction_directory = temp_dir / "_archive" extraction_directory.mkdir() - with tarfile.open(str(expected_artifact), 'r:gz') as tar_archive: + with tarfile.open(str(expected_artifact), "r:gz") as tar_archive: tar_archive.extractall(str(extraction_directory), **helpers.tarfile_extraction_compat_options()) expected_files = helpers.get_template_files( - 'sdist.standard_include', project_name, relative_root=builder.project_id + "sdist.standard_include", project_name, relative_root=builder.project_id ) helpers.assert_files(extraction_directory, expected_files) - stat = os.stat(str(extraction_directory / builder.project_id / 'PKG-INFO')) + stat = os.stat(str(extraction_directory / builder.project_id / "PKG-INFO")) assert stat.st_mtime == get_reproducible_timestamp() def test_include_license_files(self, hatch, helpers, temp_dir, config_file): - config_file.model.template.plugins['default']['src-layout'] = False + config_file.model.template.plugins["default"]["src-layout"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' + project_path = temp_dir / "my-app" config = { - 'project': {'name': project_name, 'dynamic': ['version'], 'readme': 'README.md'}, - 'tool': { - 'hatch': { - 'version': {'path': 'my_app/__about__.py'}, - 'build': {'targets': {'sdist': {'versions': ['standard'], 'include': ['my_app/', 'LICENSE.txt']}}}, + "project": {"name": project_name, "dynamic": ["version"], "readme": "README.md"}, + "tool": { + "hatch": { + "version": {"path": "my_app/__about__.py"}, + "build": {"targets": {"sdist": {"versions": ["standard"], "include": ["my_app/", "LICENSE.txt"]}}}, }, }, } builder = SdistBuilder(str(project_path), config=config) - build_path = project_path / 'dist' + build_path = project_path / "dist" with project_path.as_cwd(): artifacts = list(builder.build()) @@ -1250,42 +1250,42 @@ def test_include_license_files(self, hatch, helpers, temp_dir, config_file): build_artifacts = list(build_path.iterdir()) assert len(build_artifacts) == 1 assert expected_artifact == str(build_artifacts[0]) - assert expected_artifact == str(build_path / f'{builder.project_id}.tar.gz') + assert expected_artifact == str(build_path / f"{builder.project_id}.tar.gz") - extraction_directory = temp_dir / '_archive' + extraction_directory = temp_dir / "_archive" extraction_directory.mkdir() - with tarfile.open(str(expected_artifact), 'r:gz') as tar_archive: + with tarfile.open(str(expected_artifact), "r:gz") as tar_archive: tar_archive.extractall(str(extraction_directory), **helpers.tarfile_extraction_compat_options()) expected_files = helpers.get_template_files( - 'sdist.standard_include', project_name, relative_root=builder.project_id + "sdist.standard_include", project_name, relative_root=builder.project_id ) helpers.assert_files(extraction_directory, expected_files) - stat = os.stat(str(extraction_directory / builder.project_id / 'PKG-INFO')) + stat = os.stat(str(extraction_directory / builder.project_id / "PKG-INFO")) assert stat.st_mtime == get_reproducible_timestamp() def test_license_files_always_included(self, hatch, helpers, temp_dir, config_file): - config_file.model.template.plugins['default']['src-layout'] = False + config_file.model.template.plugins["default"]["src-layout"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' + project_path = temp_dir / "my-app" config = { - 'project': {'name': project_name, 'dynamic': ['version'], 'readme': 'README.md'}, - 'tool': { - 'hatch': { - 'version': {'path': 'my_app/__about__.py'}, - 'build': { - 'targets': { - 'sdist': {'versions': ['standard'], 'only-include': ['my_app'], 'exclude': ['LICENSE.txt']}, + "project": {"name": project_name, "dynamic": ["version"], "readme": "README.md"}, + "tool": { + "hatch": { + "version": {"path": "my_app/__about__.py"}, + "build": { + "targets": { + "sdist": {"versions": ["standard"], "only-include": ["my_app"], "exclude": ["LICENSE.txt"]}, }, }, }, @@ -1294,9 +1294,9 @@ def test_license_files_always_included(self, hatch, helpers, temp_dir, config_fi builder = SdistBuilder(str(project_path), config=config) # Ensure that only the desired readme is forcibly included - (project_path / 'my_app' / 'LICENSE.txt').touch() + (project_path / "my_app" / "LICENSE.txt").touch() - build_path = project_path / 'dist' + build_path = project_path / "dist" with project_path.as_cwd(): artifacts = list(builder.build()) @@ -1307,56 +1307,56 @@ def test_license_files_always_included(self, hatch, helpers, temp_dir, config_fi build_artifacts = list(build_path.iterdir()) assert len(build_artifacts) == 1 assert expected_artifact == str(build_artifacts[0]) - assert expected_artifact == str(build_path / f'{builder.project_id}.tar.gz') + assert expected_artifact == str(build_path / f"{builder.project_id}.tar.gz") - extraction_directory = temp_dir / '_archive' + extraction_directory = temp_dir / "_archive" extraction_directory.mkdir() - with tarfile.open(str(expected_artifact), 'r:gz') as tar_archive: + with tarfile.open(str(expected_artifact), "r:gz") as tar_archive: tar_archive.extractall(str(extraction_directory), **helpers.tarfile_extraction_compat_options()) expected_files = helpers.get_template_files( - 'sdist.standard_include', project_name, relative_root=builder.project_id + "sdist.standard_include", project_name, relative_root=builder.project_id ) helpers.assert_files(extraction_directory, expected_files) - stat = os.stat(str(extraction_directory / builder.project_id / 'PKG-INFO')) + stat = os.stat(str(extraction_directory / builder.project_id / "PKG-INFO")) assert stat.st_mtime == get_reproducible_timestamp() def test_default_vcs_git_exclusion_files(self, hatch, helpers, temp_dir, config_file): - config_file.model.template.plugins['default']['src-layout'] = False + config_file.model.template.plugins["default"]["src-layout"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' + project_path = temp_dir / "my-app" - vcs_ignore_file = temp_dir / '.gitignore' - vcs_ignore_file.write_text('*.pyc\n*.so\n*.h\n') + vcs_ignore_file = temp_dir / ".gitignore" + vcs_ignore_file.write_text("*.pyc\n*.so\n*.h\n") - (project_path / 'my_app' / 'lib.so').touch() - (project_path / 'my_app' / 'lib.h').touch() + (project_path / "my_app" / "lib.so").touch() + (project_path / "my_app" / "lib.h").touch() config = { - 'project': {'name': project_name, 'dynamic': ['version']}, - 'tool': { - 'hatch': { - 'version': {'path': 'my_app/__about__.py'}, - 'build': { - 'targets': {'sdist': {'versions': ['standard'], 'exclude': ['.gitignore']}}, - 'artifacts': ['my_app/lib.so'], + "project": {"name": project_name, "dynamic": ["version"]}, + "tool": { + "hatch": { + "version": {"path": "my_app/__about__.py"}, + "build": { + "targets": {"sdist": {"versions": ["standard"], "exclude": [".gitignore"]}}, + "artifacts": ["my_app/lib.so"], }, }, }, } builder = SdistBuilder(str(project_path), config=config) - build_path = project_path / 'dist' + build_path = project_path / "dist" build_path.mkdir() with project_path.as_cwd(): @@ -1368,33 +1368,33 @@ def test_default_vcs_git_exclusion_files(self, hatch, helpers, temp_dir, config_ build_artifacts = list(build_path.iterdir()) assert len(build_artifacts) == 1 assert expected_artifact == str(build_artifacts[0]) - assert expected_artifact == str(build_path / f'{builder.project_id}.tar.gz') + assert expected_artifact == str(build_path / f"{builder.project_id}.tar.gz") - extraction_directory = temp_dir / '_archive' + extraction_directory = temp_dir / "_archive" extraction_directory.mkdir() - with tarfile.open(str(expected_artifact), 'r:gz') as tar_archive: + with tarfile.open(str(expected_artifact), "r:gz") as tar_archive: tar_archive.extractall(str(extraction_directory), **helpers.tarfile_extraction_compat_options()) expected_files = helpers.get_template_files( - 'sdist.standard_default_vcs_git_exclusion_files', project_name, relative_root=builder.project_id + "sdist.standard_default_vcs_git_exclusion_files", project_name, relative_root=builder.project_id ) helpers.assert_files(extraction_directory, expected_files) def test_default_vcs_mercurial_exclusion_files(self, hatch, helpers, temp_dir, config_file): - config_file.model.template.plugins['default']['src-layout'] = False + config_file.model.template.plugins["default"]["src-layout"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' + project_path = temp_dir / "my-app" - vcs_ignore_file = temp_dir / '.hgignore' + vcs_ignore_file = temp_dir / ".hgignore" vcs_ignore_file.write_text( helpers.dedent( """ @@ -1411,24 +1411,24 @@ def test_default_vcs_mercurial_exclusion_files(self, hatch, helpers, temp_dir, c ) ) - (project_path / 'my_app' / 'lib.so').touch() - (project_path / 'my_app' / 'lib.h').touch() + (project_path / "my_app" / "lib.so").touch() + (project_path / "my_app" / "lib.h").touch() config = { - 'project': {'name': project_name, 'dynamic': ['version']}, - 'tool': { - 'hatch': { - 'version': {'path': 'my_app/__about__.py'}, - 'build': { - 'targets': {'sdist': {'versions': ['standard'], 'exclude': ['.hgignore']}}, - 'artifacts': ['my_app/lib.so'], + "project": {"name": project_name, "dynamic": ["version"]}, + "tool": { + "hatch": { + "version": {"path": "my_app/__about__.py"}, + "build": { + "targets": {"sdist": {"versions": ["standard"], "exclude": [".hgignore"]}}, + "artifacts": ["my_app/lib.so"], }, }, }, } builder = SdistBuilder(str(project_path), config=config) - build_path = project_path / 'dist' + build_path = project_path / "dist" build_path.mkdir() with project_path.as_cwd(): @@ -1440,43 +1440,43 @@ def test_default_vcs_mercurial_exclusion_files(self, hatch, helpers, temp_dir, c build_artifacts = list(build_path.iterdir()) assert len(build_artifacts) == 1 assert expected_artifact == str(build_artifacts[0]) - assert expected_artifact == str(build_path / f'{builder.project_id}.tar.gz') + assert expected_artifact == str(build_path / f"{builder.project_id}.tar.gz") - extraction_directory = temp_dir / '_archive' + extraction_directory = temp_dir / "_archive" extraction_directory.mkdir() - with tarfile.open(str(expected_artifact), 'r:gz') as tar_archive: + with tarfile.open(str(expected_artifact), "r:gz") as tar_archive: tar_archive.extractall(str(extraction_directory), **helpers.tarfile_extraction_compat_options()) expected_files = helpers.get_template_files( - 'sdist.standard_default_vcs_mercurial_exclusion_files', project_name, relative_root=builder.project_id + "sdist.standard_default_vcs_mercurial_exclusion_files", project_name, relative_root=builder.project_id ) helpers.assert_files(extraction_directory, expected_files) def test_no_strict_naming(self, hatch, helpers, temp_dir, config_file): - config_file.model.template.plugins['default']['src-layout'] = False + config_file.model.template.plugins["default"]["src-layout"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' + project_path = temp_dir / "my-app" config = { - 'project': {'name': project_name, 'dynamic': ['version']}, - 'tool': { - 'hatch': { - 'version': {'path': 'my_app/__about__.py'}, - 'build': {'targets': {'sdist': {'versions': ['standard'], 'strict-naming': False}}}, + "project": {"name": project_name, "dynamic": ["version"]}, + "tool": { + "hatch": { + "version": {"path": "my_app/__about__.py"}, + "build": {"targets": {"sdist": {"versions": ["standard"], "strict-naming": False}}}, }, }, } builder = SdistBuilder(str(project_path), config=config) - build_path = project_path / 'dist' + build_path = project_path / "dist" with project_path.as_cwd(): artifacts = list(builder.build()) @@ -1487,46 +1487,46 @@ def test_no_strict_naming(self, hatch, helpers, temp_dir, config_file): build_artifacts = list(build_path.iterdir()) assert len(build_artifacts) == 1 assert expected_artifact == str(build_artifacts[0]) - assert expected_artifact == str(build_path / f'{builder.artifact_project_id}.tar.gz') + assert expected_artifact == str(build_path / f"{builder.artifact_project_id}.tar.gz") - extraction_directory = temp_dir / '_archive' + extraction_directory = temp_dir / "_archive" extraction_directory.mkdir() - with tarfile.open(str(expected_artifact), 'r:gz') as tar_archive: + with tarfile.open(str(expected_artifact), "r:gz") as tar_archive: tar_archive.extractall(str(extraction_directory), **helpers.tarfile_extraction_compat_options()) expected_files = helpers.get_template_files( - 'sdist.standard_default', project_name, relative_root=builder.artifact_project_id + "sdist.standard_default", project_name, relative_root=builder.artifact_project_id ) helpers.assert_files(extraction_directory, expected_files) - stat = os.stat(str(extraction_directory / builder.artifact_project_id / 'PKG-INFO')) + stat = os.stat(str(extraction_directory / builder.artifact_project_id / "PKG-INFO")) assert stat.st_mtime == get_reproducible_timestamp() def test_file_permissions_normalized(self, hatch, temp_dir, config_file): - config_file.model.template.plugins['default']['src-layout'] = False + config_file.model.template.plugins["default"]["src-layout"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' + project_path = temp_dir / "my-app" config = { - 'project': {'name': project_name, 'dynamic': ['version']}, - 'tool': { - 'hatch': { - 'version': {'path': 'my_app/__about__.py'}, - 'build': {'targets': {'sdist': {'versions': ['standard']}}}, + "project": {"name": project_name, "dynamic": ["version"]}, + "tool": { + "hatch": { + "version": {"path": "my_app/__about__.py"}, + "build": {"targets": {"sdist": {"versions": ["standard"]}}}, }, }, } builder = SdistBuilder(str(project_path), config=config) - build_path = project_path / 'dist' + build_path = project_path / "dist" with project_path.as_cwd(): artifacts = list(builder.build()) @@ -1537,7 +1537,7 @@ def test_file_permissions_normalized(self, hatch, temp_dir, config_file): build_artifacts = list(build_path.iterdir()) assert len(build_artifacts) == 1 assert expected_artifact == str(build_artifacts[0]) - assert expected_artifact == str(build_path / f'{builder.artifact_project_id}.tar.gz') + assert expected_artifact == str(build_path / f"{builder.artifact_project_id}.tar.gz") file_stat = os.stat(expected_artifact) # we assert that at minimum 644 is set, based on the platform (e.g.) diff --git a/tests/backend/builders/test_wheel.py b/tests/backend/builders/test_wheel.py index dceb4f37c..b670b69dd 100644 --- a/tests/backend/builders/test_wheel.py +++ b/tests/backend/builders/test_wheel.py @@ -21,23 +21,23 @@ def sys_tags(): return iter( - t for t in packaging.tags.sys_tags() if 'manylinux' not in t.platform and 'muslllinux' not in t.platform + t for t in packaging.tags.sys_tags() if "manylinux" not in t.platform and "muslllinux" not in t.platform ) # https://github.com/python/cpython/pull/26184 fixed_pathlib_resolution = pytest.mark.skipif( - sys.platform == 'win32' and (sys.version_info < (3, 8) or sys.implementation.name == 'pypy'), - reason='pathlib.Path.resolve has bug on Windows', + sys.platform == "win32" and (sys.version_info < (3, 8) or sys.implementation.name == "pypy"), + reason="pathlib.Path.resolve has bug on Windows", ) def get_python_versions_tag(): - return '.'.join(f'py{major_version}' for major_version in get_known_python_major_versions()) + return ".".join(f"py{major_version}" for major_version in get_known_python_major_versions()) def extract_zip(zip_path: Path, target: Path) -> None: - with zipfile.ZipFile(zip_path, 'r') as z: + with zipfile.ZipFile(zip_path, "r") as z: for name in z.namelist(): member = z.getinfo(name) path = z.extract(member, target) @@ -51,22 +51,22 @@ def test_class(): def test_default_versions(isolation): builder = WheelBuilder(str(isolation)) - assert builder.get_default_versions() == ['standard'] + assert builder.get_default_versions() == ["standard"] class TestDefaultFileSelection: def test_already_defined(self, temp_dir): config = { - 'project': {'name': 'my-app', 'version': '0.0.1'}, - 'tool': { - 'hatch': { - 'build': { - 'targets': { - 'wheel': { - 'include': ['foo'], - 'exclude': ['bar'], - 'packages': ['foo', 'bar', 'baz'], - 'only-include': ['baz'], + "project": {"name": "my-app", "version": "0.0.1"}, + "tool": { + "hatch": { + "build": { + "targets": { + "wheel": { + "include": ["foo"], + "exclude": ["bar"], + "packages": ["foo", "bar", "baz"], + "only-include": ["baz"], } } } @@ -75,100 +75,100 @@ def test_already_defined(self, temp_dir): } builder = WheelBuilder(str(temp_dir), config=config) - assert builder.config.default_include() == ['foo'] - assert builder.config.default_exclude() == ['bar'] - assert builder.config.default_packages() == ['foo', 'bar', 'baz'] - assert builder.config.default_only_include() == ['baz'] + assert builder.config.default_include() == ["foo"] + assert builder.config.default_exclude() == ["bar"] + assert builder.config.default_packages() == ["foo", "bar", "baz"] + assert builder.config.default_only_include() == ["baz"] def test_flat_layout(self, temp_dir): config = { - 'project': {'name': 'my-app', 'version': '0.0.1'}, - 'tool': {'hatch': {'build': {'targets': {'wheel': {'exclude': ['foobarbaz']}}}}}, + "project": {"name": "my-app", "version": "0.0.1"}, + "tool": {"hatch": {"build": {"targets": {"wheel": {"exclude": ["foobarbaz"]}}}}}, } builder = WheelBuilder(str(temp_dir), config=config) - flat_root = temp_dir / 'my_app' / '__init__.py' + flat_root = temp_dir / "my_app" / "__init__.py" flat_root.ensure_parent_dir_exists() flat_root.touch() - src_root = temp_dir / 'src' / 'my_app' / '__init__.py' + src_root = temp_dir / "src" / "my_app" / "__init__.py" src_root.ensure_parent_dir_exists() src_root.touch() - single_module_root = temp_dir / 'my_app.py' + single_module_root = temp_dir / "my_app.py" single_module_root.touch() - namespace_root = temp_dir / 'ns' / 'my_app' / '__init__.py' + namespace_root = temp_dir / "ns" / "my_app" / "__init__.py" namespace_root.ensure_parent_dir_exists() namespace_root.touch() assert builder.config.default_include() == [] - assert builder.config.default_exclude() == ['foobarbaz'] - assert builder.config.default_packages() == ['my_app'] + assert builder.config.default_exclude() == ["foobarbaz"] + assert builder.config.default_packages() == ["my_app"] assert builder.config.default_only_include() == [] def test_src_layout(self, temp_dir): config = { - 'project': {'name': 'my-app', 'version': '0.0.1'}, - 'tool': {'hatch': {'build': {'targets': {'wheel': {'exclude': ['foobarbaz']}}}}}, + "project": {"name": "my-app", "version": "0.0.1"}, + "tool": {"hatch": {"build": {"targets": {"wheel": {"exclude": ["foobarbaz"]}}}}}, } builder = WheelBuilder(str(temp_dir), config=config) - src_root = temp_dir / 'src' / 'my_app' / '__init__.py' + src_root = temp_dir / "src" / "my_app" / "__init__.py" src_root.ensure_parent_dir_exists() src_root.touch() - single_module_root = temp_dir / 'my_app.py' + single_module_root = temp_dir / "my_app.py" single_module_root.touch() - namespace_root = temp_dir / 'ns' / 'my_app' / '__init__.py' + namespace_root = temp_dir / "ns" / "my_app" / "__init__.py" namespace_root.ensure_parent_dir_exists() namespace_root.touch() assert builder.config.default_include() == [] - assert builder.config.default_exclude() == ['foobarbaz'] - assert builder.config.default_packages() == ['src/my_app'] + assert builder.config.default_exclude() == ["foobarbaz"] + assert builder.config.default_packages() == ["src/my_app"] assert builder.config.default_only_include() == [] def test_single_module(self, temp_dir): config = { - 'project': {'name': 'my-app', 'version': '0.0.1'}, - 'tool': {'hatch': {'build': {'targets': {'wheel': {'exclude': ['foobarbaz']}}}}}, + "project": {"name": "my-app", "version": "0.0.1"}, + "tool": {"hatch": {"build": {"targets": {"wheel": {"exclude": ["foobarbaz"]}}}}}, } builder = WheelBuilder(str(temp_dir), config=config) - single_module_root = temp_dir / 'my_app.py' + single_module_root = temp_dir / "my_app.py" single_module_root.touch() - namespace_root = temp_dir / 'ns' / 'my_app' / '__init__.py' + namespace_root = temp_dir / "ns" / "my_app" / "__init__.py" namespace_root.ensure_parent_dir_exists() namespace_root.touch() assert builder.config.default_include() == [] - assert builder.config.default_exclude() == ['foobarbaz'] + assert builder.config.default_exclude() == ["foobarbaz"] assert builder.config.default_packages() == [] - assert builder.config.default_only_include() == ['my_app.py'] + assert builder.config.default_only_include() == ["my_app.py"] def test_namespace(self, temp_dir): config = { - 'project': {'name': 'my-app', 'version': '0.0.1'}, - 'tool': {'hatch': {'build': {'targets': {'wheel': {'exclude': ['foobarbaz']}}}}}, + "project": {"name": "my-app", "version": "0.0.1"}, + "tool": {"hatch": {"build": {"targets": {"wheel": {"exclude": ["foobarbaz"]}}}}}, } builder = WheelBuilder(str(temp_dir), config=config) - namespace_root = temp_dir / 'ns' / 'my_app' / '__init__.py' + namespace_root = temp_dir / "ns" / "my_app" / "__init__.py" namespace_root.ensure_parent_dir_exists() namespace_root.touch() assert builder.config.default_include() == [] - assert builder.config.default_exclude() == ['foobarbaz'] - assert builder.config.default_packages() == ['ns'] + assert builder.config.default_exclude() == ["foobarbaz"] + assert builder.config.default_packages() == ["ns"] assert builder.config.default_only_include() == [] def test_default_error(self, temp_dir): config = { - 'project': {'name': 'MyApp', 'version': '0.0.1'}, - 'tool': {'hatch': {'build': {'targets': {'wheel': {'exclude': ['foobarbaz']}}}}}, + "project": {"name": "MyApp", "version": "0.0.1"}, + "tool": {"hatch": {"build": {"targets": {"wheel": {"exclude": ["foobarbaz"]}}}}}, } builder = WheelBuilder(str(temp_dir), config=config) @@ -181,15 +181,15 @@ def test_default_error(self, temp_dir): with pytest.raises( ValueError, match=( - 'Unable to determine which files to ship inside the wheel using the following heuristics: ' - 'https://hatch.pypa.io/latest/plugins/builder/wheel/#default-file-selection\n\n' - 'The most likely cause of this is that there is no directory that matches the name of your ' - 'project \\(MyApp or myapp\\).\n\n' - 'At least one file selection option must be defined in the `tool.hatch.build.targets.wheel` ' - 'table, see: https://hatch.pypa.io/latest/config/build/\n\n' - 'As an example, if you intend to ship a directory named `foo` that resides within a `src` ' - 'directory located at the root of your project, you can define the following:\n\n' - '\\[tool.hatch.build.targets.wheel\\]\n' + "Unable to determine which files to ship inside the wheel using the following heuristics: " + "https://hatch.pypa.io/latest/plugins/builder/wheel/#default-file-selection\n\n" + "The most likely cause of this is that there is no directory that matches the name of your " + "project \\(MyApp or myapp\\).\n\n" + "At least one file selection option must be defined in the `tool.hatch.build.targets.wheel` " + "table, see: https://hatch.pypa.io/latest/config/build/\n\n" + "As an example, if you intend to ship a directory named `foo` that resides within a `src` " + "directory located at the root of your project, you can define the following:\n\n" + "\\[tool.hatch.build.targets.wheel\\]\n" 'packages = \\["src/foo"\\]' ), ): @@ -197,8 +197,8 @@ def test_default_error(self, temp_dir): def test_bypass_selection_option(self, temp_dir): config = { - 'project': {'name': 'my-app', 'version': '0.0.1'}, - 'tool': {'hatch': {'build': {'targets': {'wheel': {'bypass-selection': True}}}}}, + "project": {"name": "my-app", "version": "0.0.1"}, + "tool": {"hatch": {"build": {"targets": {"wheel": {"bypass-selection": True}}}}}, } builder = WheelBuilder(str(temp_dir), config=config) @@ -209,8 +209,8 @@ def test_bypass_selection_option(self, temp_dir): def test_force_include_option_considered_selection(self, temp_dir): config = { - 'project': {'name': 'my-app', 'version': '0.0.1'}, - 'tool': {'hatch': {'build': {'targets': {'wheel': {'force-include': {'foo': 'bar'}}}}}}, + "project": {"name": "my-app", "version": "0.0.1"}, + "tool": {"hatch": {"build": {"targets": {"wheel": {"force-include": {"foo": "bar"}}}}}}, } builder = WheelBuilder(str(temp_dir), config=config) @@ -220,10 +220,10 @@ def test_force_include_option_considered_selection(self, temp_dir): assert builder.config.default_only_include() == [] def test_force_include_build_data_considered_selection(self, temp_dir): - config = {'project': {'name': 'my-app', 'version': '0.0.1'}} + config = {"project": {"name": "my-app", "version": "0.0.1"}} builder = WheelBuilder(str(temp_dir), config=config) - build_data = {'artifacts': [], 'force_include': {'foo': 'bar'}} + build_data = {"artifacts": [], "force_include": {"foo": "bar"}} with builder.config.set_build_data(build_data): assert builder.config.default_include() == [] assert builder.config.default_exclude() == [] @@ -231,10 +231,10 @@ def test_force_include_build_data_considered_selection(self, temp_dir): assert builder.config.default_only_include() == [] def test_artifacts_build_data_considered_selection(self, temp_dir): - config = {'project': {'name': 'my-app', 'version': '0.0.1'}} + config = {"project": {"name": "my-app", "version": "0.0.1"}} builder = WheelBuilder(str(temp_dir), config=config) - build_data = {'artifacts': ['foo'], 'force_include': {}} + build_data = {"artifacts": ["foo"], "force_include": {}} with builder.config.set_build_data(build_data): assert builder.config.default_include() == [] assert builder.config.default_exclude() == [] @@ -242,24 +242,24 @@ def test_artifacts_build_data_considered_selection(self, temp_dir): assert builder.config.default_only_include() == [] def test_unnormalized_name_with_unnormalized_directory(self, temp_dir): - config = {'project': {'name': 'MyApp', 'version': '0.0.1'}} + config = {"project": {"name": "MyApp", "version": "0.0.1"}} builder = WheelBuilder(str(temp_dir), config=config) - src_root = temp_dir / 'src' / 'MyApp' / '__init__.py' + src_root = temp_dir / "src" / "MyApp" / "__init__.py" src_root.ensure_parent_dir_exists() src_root.touch() - assert builder.config.default_packages() == ['src/MyApp'] + assert builder.config.default_packages() == ["src/MyApp"] def test_unnormalized_name_with_normalized_directory(self, temp_dir): - config = {'project': {'name': 'MyApp', 'version': '0.0.1'}} + config = {"project": {"name": "MyApp", "version": "0.0.1"}} builder = WheelBuilder(str(temp_dir), config=config) - src_root = temp_dir / 'src' / 'myapp' / '__init__.py' + src_root = temp_dir / "src" / "myapp" / "__init__.py" src_root.ensure_parent_dir_exists() src_root.touch() - assert builder.config.default_packages() == ['src/myapp'] + assert builder.config.default_packages() == ["src/myapp"] class TestCoreMetadataConstructor: @@ -270,23 +270,23 @@ def test_default(self, isolation): assert builder.config.core_metadata_constructor is get_core_metadata_constructors()[DEFAULT_METADATA_VERSION] def test_not_string(self, isolation): - config = {'tool': {'hatch': {'build': {'targets': {'wheel': {'core-metadata-version': 42}}}}}} + config = {"tool": {"hatch": {"build": {"targets": {"wheel": {"core-metadata-version": 42}}}}}} builder = WheelBuilder(str(isolation), config=config) with pytest.raises( - TypeError, match='Field `tool.hatch.build.targets.wheel.core-metadata-version` must be a string' + TypeError, match="Field `tool.hatch.build.targets.wheel.core-metadata-version` must be a string" ): _ = builder.config.core_metadata_constructor def test_unknown(self, isolation): - config = {'tool': {'hatch': {'build': {'targets': {'wheel': {'core-metadata-version': '9000'}}}}}} + config = {"tool": {"hatch": {"build": {"targets": {"wheel": {"core-metadata-version": "9000"}}}}}} builder = WheelBuilder(str(isolation), config=config) with pytest.raises( ValueError, match=( - f'Unknown metadata version `9000` for field `tool.hatch.build.targets.wheel.core-metadata-version`. ' - f'Available: {", ".join(sorted(get_core_metadata_constructors()))}' + f"Unknown metadata version `9000` for field `tool.hatch.build.targets.wheel.core-metadata-version`. " + f"Available: {', '.join(sorted(get_core_metadata_constructors()))}" ), ): _ = builder.config.core_metadata_constructor @@ -299,72 +299,72 @@ def test_default(self, isolation): assert builder.config.shared_data == builder.config.shared_data == {} def test_invalid_type(self, isolation): - config = {'tool': {'hatch': {'build': {'targets': {'wheel': {'shared-data': 42}}}}}} + config = {"tool": {"hatch": {"build": {"targets": {"wheel": {"shared-data": 42}}}}}} builder = WheelBuilder(str(isolation), config=config) - with pytest.raises(TypeError, match='Field `tool.hatch.build.targets.wheel.shared-data` must be a mapping'): + with pytest.raises(TypeError, match="Field `tool.hatch.build.targets.wheel.shared-data` must be a mapping"): _ = builder.config.shared_data def test_absolute(self, isolation): config = { - 'tool': { - 'hatch': {'build': {'targets': {'wheel': {'shared-data': {str(isolation / 'source'): '/target/'}}}}} + "tool": { + "hatch": {"build": {"targets": {"wheel": {"shared-data": {str(isolation / "source"): "/target/"}}}}} } } builder = WheelBuilder(str(isolation), config=config) - assert builder.config.shared_data == {str(isolation / 'source'): 'target'} + assert builder.config.shared_data == {str(isolation / "source"): "target"} def test_relative(self, isolation): - config = {'tool': {'hatch': {'build': {'targets': {'wheel': {'shared-data': {'../source': '/target/'}}}}}}} - builder = WheelBuilder(str(isolation / 'foo'), config=config) + config = {"tool": {"hatch": {"build": {"targets": {"wheel": {"shared-data": {"../source": "/target/"}}}}}}} + builder = WheelBuilder(str(isolation / "foo"), config=config) - assert builder.config.shared_data == {str(isolation / 'source'): 'target'} + assert builder.config.shared_data == {str(isolation / "source"): "target"} def test_source_empty_string(self, isolation): - config = {'tool': {'hatch': {'build': {'targets': {'wheel': {'shared-data': {'': '/target/'}}}}}}} + config = {"tool": {"hatch": {"build": {"targets": {"wheel": {"shared-data": {"": "/target/"}}}}}}} builder = WheelBuilder(str(isolation), config=config) with pytest.raises( ValueError, - match='Source #1 in field `tool.hatch.build.targets.wheel.shared-data` cannot be an empty string', + match="Source #1 in field `tool.hatch.build.targets.wheel.shared-data` cannot be an empty string", ): _ = builder.config.shared_data def test_relative_path_not_string(self, isolation): - config = {'tool': {'hatch': {'build': {'targets': {'wheel': {'shared-data': {'source': 0}}}}}}} + config = {"tool": {"hatch": {"build": {"targets": {"wheel": {"shared-data": {"source": 0}}}}}}} builder = WheelBuilder(str(isolation), config=config) with pytest.raises( TypeError, - match='Path for source `source` in field `tool.hatch.build.targets.wheel.shared-data` must be a string', + match="Path for source `source` in field `tool.hatch.build.targets.wheel.shared-data` must be a string", ): _ = builder.config.shared_data def test_relative_path_empty_string(self, isolation): - config = {'tool': {'hatch': {'build': {'targets': {'wheel': {'shared-data': {'source': ''}}}}}}} + config = {"tool": {"hatch": {"build": {"targets": {"wheel": {"shared-data": {"source": ""}}}}}}} builder = WheelBuilder(str(isolation), config=config) with pytest.raises( ValueError, match=( - 'Path for source `source` in field `tool.hatch.build.targets.wheel.shared-data` ' - 'cannot be an empty string' + "Path for source `source` in field `tool.hatch.build.targets.wheel.shared-data` " + "cannot be an empty string" ), ): _ = builder.config.shared_data def test_order(self, isolation): config = { - 'tool': { - 'hatch': { - 'build': { - 'targets': { - 'wheel': { - 'shared-data': { - '../very-nested': 'target1/embedded', - '../source1': '/target2/', - '../source2': '/target1/', + "tool": { + "hatch": { + "build": { + "targets": { + "wheel": { + "shared-data": { + "../very-nested": "target1/embedded", + "../source1": "/target2/", + "../source2": "/target1/", } } } @@ -372,12 +372,12 @@ def test_order(self, isolation): } } } - builder = WheelBuilder(str(isolation / 'foo'), config=config) + builder = WheelBuilder(str(isolation / "foo"), config=config) assert builder.config.shared_data == { - str(isolation / 'source2'): 'target1', - str(isolation / 'very-nested'): f'target1{os.sep}embedded', - str(isolation / 'source1'): 'target2', + str(isolation / "source2"): "target1", + str(isolation / "very-nested"): f"target1{os.sep}embedded", + str(isolation / "source1"): "target2", } @@ -388,72 +388,72 @@ def test_default(self, isolation): assert builder.config.shared_scripts == builder.config.shared_scripts == {} def test_invalid_type(self, isolation): - config = {'tool': {'hatch': {'build': {'targets': {'wheel': {'shared-scripts': 42}}}}}} + config = {"tool": {"hatch": {"build": {"targets": {"wheel": {"shared-scripts": 42}}}}}} builder = WheelBuilder(str(isolation), config=config) - with pytest.raises(TypeError, match='Field `tool.hatch.build.targets.wheel.shared-scripts` must be a mapping'): + with pytest.raises(TypeError, match="Field `tool.hatch.build.targets.wheel.shared-scripts` must be a mapping"): _ = builder.config.shared_scripts def test_absolute(self, isolation): config = { - 'tool': { - 'hatch': {'build': {'targets': {'wheel': {'shared-scripts': {str(isolation / 'source'): '/target/'}}}}} + "tool": { + "hatch": {"build": {"targets": {"wheel": {"shared-scripts": {str(isolation / "source"): "/target/"}}}}} } } builder = WheelBuilder(str(isolation), config=config) - assert builder.config.shared_scripts == {str(isolation / 'source'): 'target'} + assert builder.config.shared_scripts == {str(isolation / "source"): "target"} def test_relative(self, isolation): - config = {'tool': {'hatch': {'build': {'targets': {'wheel': {'shared-scripts': {'../source': '/target/'}}}}}}} - builder = WheelBuilder(str(isolation / 'foo'), config=config) + config = {"tool": {"hatch": {"build": {"targets": {"wheel": {"shared-scripts": {"../source": "/target/"}}}}}}} + builder = WheelBuilder(str(isolation / "foo"), config=config) - assert builder.config.shared_scripts == {str(isolation / 'source'): 'target'} + assert builder.config.shared_scripts == {str(isolation / "source"): "target"} def test_source_empty_string(self, isolation): - config = {'tool': {'hatch': {'build': {'targets': {'wheel': {'shared-scripts': {'': '/target/'}}}}}}} + config = {"tool": {"hatch": {"build": {"targets": {"wheel": {"shared-scripts": {"": "/target/"}}}}}}} builder = WheelBuilder(str(isolation), config=config) with pytest.raises( ValueError, - match='Source #1 in field `tool.hatch.build.targets.wheel.shared-scripts` cannot be an empty string', + match="Source #1 in field `tool.hatch.build.targets.wheel.shared-scripts` cannot be an empty string", ): _ = builder.config.shared_scripts def test_relative_path_not_string(self, isolation): - config = {'tool': {'hatch': {'build': {'targets': {'wheel': {'shared-scripts': {'source': 0}}}}}}} + config = {"tool": {"hatch": {"build": {"targets": {"wheel": {"shared-scripts": {"source": 0}}}}}}} builder = WheelBuilder(str(isolation), config=config) with pytest.raises( TypeError, - match='Path for source `source` in field `tool.hatch.build.targets.wheel.shared-scripts` must be a string', + match="Path for source `source` in field `tool.hatch.build.targets.wheel.shared-scripts` must be a string", ): _ = builder.config.shared_scripts def test_relative_path_empty_string(self, isolation): - config = {'tool': {'hatch': {'build': {'targets': {'wheel': {'shared-scripts': {'source': ''}}}}}}} + config = {"tool": {"hatch": {"build": {"targets": {"wheel": {"shared-scripts": {"source": ""}}}}}}} builder = WheelBuilder(str(isolation), config=config) with pytest.raises( ValueError, match=( - 'Path for source `source` in field `tool.hatch.build.targets.wheel.shared-scripts` ' - 'cannot be an empty string' + "Path for source `source` in field `tool.hatch.build.targets.wheel.shared-scripts` " + "cannot be an empty string" ), ): _ = builder.config.shared_scripts def test_order(self, isolation): config = { - 'tool': { - 'hatch': { - 'build': { - 'targets': { - 'wheel': { - 'shared-scripts': { - '../very-nested': 'target1/embedded', - '../source1': '/target2/', - '../source2': '/target1/', + "tool": { + "hatch": { + "build": { + "targets": { + "wheel": { + "shared-scripts": { + "../very-nested": "target1/embedded", + "../source1": "/target2/", + "../source2": "/target1/", } } } @@ -461,12 +461,12 @@ def test_order(self, isolation): } } } - builder = WheelBuilder(str(isolation / 'foo'), config=config) + builder = WheelBuilder(str(isolation / "foo"), config=config) assert builder.config.shared_scripts == { - str(isolation / 'source2'): 'target1', - str(isolation / 'very-nested'): f'target1{os.sep}embedded', - str(isolation / 'source1'): 'target2', + str(isolation / "source2"): "target1", + str(isolation / "very-nested"): f"target1{os.sep}embedded", + str(isolation / "source1"): "target2", } @@ -477,72 +477,72 @@ def test_default(self, isolation): assert builder.config.extra_metadata == builder.config.extra_metadata == {} def test_invalid_type(self, isolation): - config = {'tool': {'hatch': {'build': {'targets': {'wheel': {'extra-metadata': 42}}}}}} + config = {"tool": {"hatch": {"build": {"targets": {"wheel": {"extra-metadata": 42}}}}}} builder = WheelBuilder(str(isolation), config=config) - with pytest.raises(TypeError, match='Field `tool.hatch.build.targets.wheel.extra-metadata` must be a mapping'): + with pytest.raises(TypeError, match="Field `tool.hatch.build.targets.wheel.extra-metadata` must be a mapping"): _ = builder.config.extra_metadata def test_absolute(self, isolation): config = { - 'tool': { - 'hatch': {'build': {'targets': {'wheel': {'extra-metadata': {str(isolation / 'source'): '/target/'}}}}} + "tool": { + "hatch": {"build": {"targets": {"wheel": {"extra-metadata": {str(isolation / "source"): "/target/"}}}}} } } builder = WheelBuilder(str(isolation), config=config) - assert builder.config.extra_metadata == {str(isolation / 'source'): 'target'} + assert builder.config.extra_metadata == {str(isolation / "source"): "target"} def test_relative(self, isolation): - config = {'tool': {'hatch': {'build': {'targets': {'wheel': {'extra-metadata': {'../source': '/target/'}}}}}}} - builder = WheelBuilder(str(isolation / 'foo'), config=config) + config = {"tool": {"hatch": {"build": {"targets": {"wheel": {"extra-metadata": {"../source": "/target/"}}}}}}} + builder = WheelBuilder(str(isolation / "foo"), config=config) - assert builder.config.extra_metadata == {str(isolation / 'source'): 'target'} + assert builder.config.extra_metadata == {str(isolation / "source"): "target"} def test_source_empty_string(self, isolation): - config = {'tool': {'hatch': {'build': {'targets': {'wheel': {'extra-metadata': {'': '/target/'}}}}}}} + config = {"tool": {"hatch": {"build": {"targets": {"wheel": {"extra-metadata": {"": "/target/"}}}}}}} builder = WheelBuilder(str(isolation), config=config) with pytest.raises( ValueError, - match='Source #1 in field `tool.hatch.build.targets.wheel.extra-metadata` cannot be an empty string', + match="Source #1 in field `tool.hatch.build.targets.wheel.extra-metadata` cannot be an empty string", ): _ = builder.config.extra_metadata def test_relative_path_not_string(self, isolation): - config = {'tool': {'hatch': {'build': {'targets': {'wheel': {'extra-metadata': {'source': 0}}}}}}} + config = {"tool": {"hatch": {"build": {"targets": {"wheel": {"extra-metadata": {"source": 0}}}}}}} builder = WheelBuilder(str(isolation), config=config) with pytest.raises( TypeError, - match='Path for source `source` in field `tool.hatch.build.targets.wheel.extra-metadata` must be a string', + match="Path for source `source` in field `tool.hatch.build.targets.wheel.extra-metadata` must be a string", ): _ = builder.config.extra_metadata def test_relative_path_empty_string(self, isolation): - config = {'tool': {'hatch': {'build': {'targets': {'wheel': {'extra-metadata': {'source': ''}}}}}}} + config = {"tool": {"hatch": {"build": {"targets": {"wheel": {"extra-metadata": {"source": ""}}}}}}} builder = WheelBuilder(str(isolation), config=config) with pytest.raises( ValueError, match=( - 'Path for source `source` in field `tool.hatch.build.targets.wheel.extra-metadata` ' - 'cannot be an empty string' + "Path for source `source` in field `tool.hatch.build.targets.wheel.extra-metadata` " + "cannot be an empty string" ), ): _ = builder.config.extra_metadata def test_order(self, isolation): config = { - 'tool': { - 'hatch': { - 'build': { - 'targets': { - 'wheel': { - 'extra-metadata': { - '../very-nested': 'target1/embedded', - '../source1': '/target2/', - '../source2': '/target1/', + "tool": { + "hatch": { + "build": { + "targets": { + "wheel": { + "extra-metadata": { + "../very-nested": "target1/embedded", + "../source1": "/target2/", + "../source2": "/target1/", } } } @@ -550,12 +550,12 @@ def test_order(self, isolation): } } } - builder = WheelBuilder(str(isolation / 'foo'), config=config) + builder = WheelBuilder(str(isolation / "foo"), config=config) assert builder.config.extra_metadata == { - str(isolation / 'source2'): 'target1', - str(isolation / 'very-nested'): f'target1{os.sep}embedded', - str(isolation / 'source1'): 'target2', + str(isolation / "source2"): "target1", + str(isolation / "very-nested"): f"target1{os.sep}embedded", + str(isolation / "source1"): "target2", } @@ -566,33 +566,33 @@ def test_default(self, isolation): assert builder.config.strict_naming is builder.config.strict_naming is True def test_target(self, isolation): - config = {'tool': {'hatch': {'build': {'targets': {'wheel': {'strict-naming': False}}}}}} + config = {"tool": {"hatch": {"build": {"targets": {"wheel": {"strict-naming": False}}}}}} builder = WheelBuilder(str(isolation), config=config) assert builder.config.strict_naming is False def test_target_not_boolean(self, isolation): - config = {'tool': {'hatch': {'build': {'targets': {'wheel': {'strict-naming': 9000}}}}}} + config = {"tool": {"hatch": {"build": {"targets": {"wheel": {"strict-naming": 9000}}}}}} builder = WheelBuilder(str(isolation), config=config) - with pytest.raises(TypeError, match='Field `tool.hatch.build.targets.wheel.strict-naming` must be a boolean'): + with pytest.raises(TypeError, match="Field `tool.hatch.build.targets.wheel.strict-naming` must be a boolean"): _ = builder.config.strict_naming def test_global(self, isolation): - config = {'tool': {'hatch': {'build': {'strict-naming': False}}}} + config = {"tool": {"hatch": {"build": {"strict-naming": False}}}} builder = WheelBuilder(str(isolation), config=config) assert builder.config.strict_naming is False def test_global_not_boolean(self, isolation): - config = {'tool': {'hatch': {'build': {'strict-naming': 9000}}}} + config = {"tool": {"hatch": {"build": {"strict-naming": 9000}}}} builder = WheelBuilder(str(isolation), config=config) - with pytest.raises(TypeError, match='Field `tool.hatch.build.strict-naming` must be a boolean'): + with pytest.raises(TypeError, match="Field `tool.hatch.build.strict-naming` must be a boolean"): _ = builder.config.strict_naming def test_target_overrides_global(self, isolation): - config = {'tool': {'hatch': {'build': {'strict-naming': False, 'targets': {'wheel': {'strict-naming': True}}}}}} + config = {"tool": {"hatch": {"build": {"strict-naming": False, "targets": {"wheel": {"strict-naming": True}}}}}} builder = WheelBuilder(str(isolation), config=config) assert builder.config.strict_naming is True @@ -605,17 +605,17 @@ def test_default(self, isolation): assert builder.config.macos_max_compat is builder.config.macos_max_compat is False def test_correct(self, isolation): - config = {'tool': {'hatch': {'build': {'targets': {'wheel': {'macos-max-compat': True}}}}}} + config = {"tool": {"hatch": {"build": {"targets": {"wheel": {"macos-max-compat": True}}}}}} builder = WheelBuilder(str(isolation), config=config) assert builder.config.macos_max_compat is True def test_not_boolean(self, isolation): - config = {'tool': {'hatch': {'build': {'targets': {'wheel': {'macos-max-compat': 9000}}}}}} + config = {"tool": {"hatch": {"build": {"targets": {"wheel": {"macos-max-compat": 9000}}}}}} builder = WheelBuilder(str(isolation), config=config) with pytest.raises( - TypeError, match='Field `tool.hatch.build.targets.wheel.macos-max-compat` must be a boolean' + TypeError, match="Field `tool.hatch.build.targets.wheel.macos-max-compat` must be a boolean" ): _ = builder.config.macos_max_compat @@ -627,30 +627,30 @@ def test_default(self, isolation): assert builder.config.bypass_selection is False def test_correct(self, isolation): - config = {'tool': {'hatch': {'build': {'targets': {'wheel': {'bypass-selection': True}}}}}} + config = {"tool": {"hatch": {"build": {"targets": {"wheel": {"bypass-selection": True}}}}}} builder = WheelBuilder(str(isolation), config=config) assert builder.config.bypass_selection is True def test_not_boolean(self, isolation): - config = {'tool': {'hatch': {'build': {'targets': {'wheel': {'bypass-selection': 9000}}}}}} + config = {"tool": {"hatch": {"build": {"targets": {"wheel": {"bypass-selection": 9000}}}}}} builder = WheelBuilder(str(isolation), config=config) with pytest.raises( - TypeError, match='Field `tool.hatch.build.targets.wheel.bypass-selection` must be a boolean' + TypeError, match="Field `tool.hatch.build.targets.wheel.bypass-selection` must be a boolean" ): _ = builder.config.bypass_selection class TestConstructEntryPointsFile: def test_default(self, isolation): - config = {'project': {}} + config = {"project": {}} builder = WheelBuilder(str(isolation), config=config) - assert builder.construct_entry_points_file() == '' + assert builder.construct_entry_points_file() == "" def test_scripts(self, isolation, helpers): - config = {'project': {'scripts': {'foo': 'pkg:bar', 'bar': 'pkg:foo'}}} + config = {"project": {"scripts": {"foo": "pkg:bar", "bar": "pkg:foo"}}} builder = WheelBuilder(str(isolation), config=config) assert builder.construct_entry_points_file() == helpers.dedent( @@ -662,7 +662,7 @@ def test_scripts(self, isolation, helpers): ) def test_gui_scripts(self, isolation, helpers): - config = {'project': {'gui-scripts': {'foo': 'pkg:bar', 'bar': 'pkg:foo'}}} + config = {"project": {"gui-scripts": {"foo": "pkg:bar", "bar": "pkg:foo"}}} builder = WheelBuilder(str(isolation), config=config) assert builder.construct_entry_points_file() == helpers.dedent( @@ -675,10 +675,10 @@ def test_gui_scripts(self, isolation, helpers): def test_entry_points(self, isolation, helpers): config = { - 'project': { - 'entry-points': { - 'foo': {'bar': 'pkg:foo', 'foo': 'pkg:bar'}, - 'bar': {'foo': 'pkg:bar', 'bar': 'pkg:foo'}, + "project": { + "entry-points": { + "foo": {"bar": "pkg:foo", "foo": "pkg:bar"}, + "bar": {"foo": "pkg:bar", "bar": "pkg:foo"}, } } } @@ -698,12 +698,12 @@ def test_entry_points(self, isolation, helpers): def test_all(self, isolation, helpers): config = { - 'project': { - 'scripts': {'foo': 'pkg:bar', 'bar': 'pkg:foo'}, - 'gui-scripts': {'foo': 'pkg:bar', 'bar': 'pkg:foo'}, - 'entry-points': { - 'foo': {'bar': 'pkg:foo', 'foo': 'pkg:bar'}, - 'bar': {'foo': 'pkg:bar', 'bar': 'pkg:foo'}, + "project": { + "scripts": {"foo": "pkg:bar", "bar": "pkg:foo"}, + "gui-scripts": {"foo": "pkg:bar", "bar": "pkg:foo"}, + "entry-points": { + "foo": {"bar": "pkg:foo", "foo": "pkg:bar"}, + "bar": {"foo": "pkg:bar", "bar": "pkg:foo"}, }, } } @@ -732,30 +732,30 @@ def test_all(self, isolation, helpers): class TestBuildStandard: def test_default_auto_detection(self, hatch, helpers, temp_dir, config_file): - config_file.model.template.plugins['default']['src-layout'] = False + config_file.model.template.plugins["default"]["src-layout"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' + project_path = temp_dir / "my-app" config = { - 'project': {'name': project_name, 'dynamic': ['version']}, - 'tool': { - 'hatch': { - 'version': {'path': 'my_app/__about__.py'}, - 'build': {'targets': {'wheel': {'versions': ['standard']}}}, + "project": {"name": project_name, "dynamic": ["version"]}, + "tool": { + "hatch": { + "version": {"path": "my_app/__about__.py"}, + "build": {"targets": {"wheel": {"versions": ["standard"]}}}, }, }, } builder = WheelBuilder(str(project_path), config=config) - build_path = project_path / 'dist' + build_path = project_path / "dist" with project_path.as_cwd(): artifacts = list(builder.build()) @@ -766,53 +766,53 @@ def test_default_auto_detection(self, hatch, helpers, temp_dir, config_file): build_artifacts = list(build_path.iterdir()) assert len(build_artifacts) == 1 assert expected_artifact == str(build_artifacts[0]) - assert expected_artifact == str(build_path / f'{builder.project_id}-{get_python_versions_tag()}-none-any.whl') + assert expected_artifact == str(build_path / f"{builder.project_id}-{get_python_versions_tag()}-none-any.whl") - extraction_directory = temp_dir / '_archive' + extraction_directory = temp_dir / "_archive" extraction_directory.mkdir() - with zipfile.ZipFile(str(expected_artifact), 'r') as zip_archive: + with zipfile.ZipFile(str(expected_artifact), "r") as zip_archive: zip_archive.extractall(str(extraction_directory)) - metadata_directory = f'{builder.project_id}.dist-info' + metadata_directory = f"{builder.project_id}.dist-info" expected_files = helpers.get_template_files( - 'wheel.standard_default_license_single', project_name, metadata_directory=metadata_directory + "wheel.standard_default_license_single", project_name, metadata_directory=metadata_directory ) helpers.assert_files(extraction_directory, expected_files) # Inspect the archive rather than the extracted files because on Windows they lose their metadata # https://stackoverflow.com/q/9813243 - with zipfile.ZipFile(str(expected_artifact), 'r') as zip_archive: - zip_info = zip_archive.getinfo(f'{metadata_directory}/WHEEL') + with zipfile.ZipFile(str(expected_artifact), "r") as zip_archive: + zip_info = zip_archive.getinfo(f"{metadata_directory}/WHEEL") assert zip_info.date_time == (2020, 2, 2, 0, 0, 0) def test_default_reproducible_timestamp(self, hatch, helpers, temp_dir, config_file): - config_file.model.template.plugins['default']['src-layout'] = False + config_file.model.template.plugins["default"]["src-layout"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' + project_path = temp_dir / "my-app" config = { - 'project': {'name': project_name, 'dynamic': ['version']}, - 'tool': { - 'hatch': { - 'version': {'path': 'my_app/__about__.py'}, - 'build': {'targets': {'wheel': {'versions': ['standard']}}}, + "project": {"name": project_name, "dynamic": ["version"]}, + "tool": { + "hatch": { + "version": {"path": "my_app/__about__.py"}, + "build": {"targets": {"wheel": {"versions": ["standard"]}}}, }, }, } builder = WheelBuilder(str(project_path), config=config) - build_path = project_path / 'dist' + build_path = project_path / "dist" build_path.mkdir() - with project_path.as_cwd(env_vars={'SOURCE_DATE_EPOCH': '1580601700'}): + with project_path.as_cwd(env_vars={"SOURCE_DATE_EPOCH": "1580601700"}): artifacts = list(builder.build(directory=str(build_path))) assert len(artifacts) == 1 @@ -821,51 +821,51 @@ def test_default_reproducible_timestamp(self, hatch, helpers, temp_dir, config_f build_artifacts = list(build_path.iterdir()) assert len(build_artifacts) == 1 assert expected_artifact == str(build_artifacts[0]) - assert expected_artifact == str(build_path / f'{builder.project_id}-{get_python_versions_tag()}-none-any.whl') + assert expected_artifact == str(build_path / f"{builder.project_id}-{get_python_versions_tag()}-none-any.whl") - extraction_directory = temp_dir / '_archive' + extraction_directory = temp_dir / "_archive" extraction_directory.mkdir() - with zipfile.ZipFile(str(expected_artifact), 'r') as zip_archive: + with zipfile.ZipFile(str(expected_artifact), "r") as zip_archive: zip_archive.extractall(str(extraction_directory)) - metadata_directory = f'{builder.project_id}.dist-info' + metadata_directory = f"{builder.project_id}.dist-info" expected_files = helpers.get_template_files( - 'wheel.standard_default_license_single', project_name, metadata_directory=metadata_directory + "wheel.standard_default_license_single", project_name, metadata_directory=metadata_directory ) helpers.assert_files(extraction_directory, expected_files) - with zipfile.ZipFile(str(expected_artifact), 'r') as zip_archive: - zip_info = zip_archive.getinfo(f'{metadata_directory}/WHEEL') + with zipfile.ZipFile(str(expected_artifact), "r") as zip_archive: + zip_info = zip_archive.getinfo(f"{metadata_directory}/WHEEL") assert zip_info.date_time == (2020, 2, 2, 0, 1, 40) def test_default_no_reproducible(self, hatch, helpers, temp_dir, config_file): - config_file.model.template.plugins['default']['src-layout'] = False + config_file.model.template.plugins["default"]["src-layout"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' + project_path = temp_dir / "my-app" config = { - 'project': {'name': project_name, 'dynamic': ['version']}, - 'tool': { - 'hatch': { - 'version': {'path': 'my_app/__about__.py'}, - 'build': {'targets': {'wheel': {'versions': ['standard'], 'reproducible': False}}}, + "project": {"name": project_name, "dynamic": ["version"]}, + "tool": { + "hatch": { + "version": {"path": "my_app/__about__.py"}, + "build": {"targets": {"wheel": {"versions": ["standard"], "reproducible": False}}}, }, }, } builder = WheelBuilder(str(project_path), config=config) - build_path = project_path / 'dist' + build_path = project_path / "dist" build_path.mkdir() - with project_path.as_cwd(env_vars={'SOURCE_DATE_EPOCH': '1580601700'}): + with project_path.as_cwd(env_vars={"SOURCE_DATE_EPOCH": "1580601700"}): artifacts = list(builder.build(directory=str(build_path))) assert len(artifacts) == 1 @@ -874,52 +874,52 @@ def test_default_no_reproducible(self, hatch, helpers, temp_dir, config_file): build_artifacts = list(build_path.iterdir()) assert len(build_artifacts) == 1 assert expected_artifact == str(build_artifacts[0]) - assert expected_artifact == str(build_path / f'{builder.project_id}-{get_python_versions_tag()}-none-any.whl') + assert expected_artifact == str(build_path / f"{builder.project_id}-{get_python_versions_tag()}-none-any.whl") - extraction_directory = temp_dir / '_archive' + extraction_directory = temp_dir / "_archive" extraction_directory.mkdir() - with zipfile.ZipFile(str(expected_artifact), 'r') as zip_archive: + with zipfile.ZipFile(str(expected_artifact), "r") as zip_archive: zip_archive.extractall(str(extraction_directory)) - metadata_directory = f'{builder.project_id}.dist-info' + metadata_directory = f"{builder.project_id}.dist-info" expected_files = helpers.get_template_files( - 'wheel.standard_default_license_single', project_name, metadata_directory=metadata_directory + "wheel.standard_default_license_single", project_name, metadata_directory=metadata_directory ) helpers.assert_files(extraction_directory, expected_files) - with zipfile.ZipFile(str(expected_artifact), 'r') as zip_archive: - zip_info = zip_archive.getinfo(f'{metadata_directory}/WHEEL') + with zipfile.ZipFile(str(expected_artifact), "r") as zip_archive: + zip_info = zip_archive.getinfo(f"{metadata_directory}/WHEEL") assert zip_info.date_time == (2020, 2, 2, 0, 0, 0) def test_default_multiple_licenses(self, hatch, helpers, config_file, temp_dir): - project_name = 'My.App' - config_file.model.template.plugins['default']['src-layout'] = False - config_file.model.template.licenses.default = ['MIT', 'Apache-2.0'] + project_name = "My.App" + config_file.model.template.plugins["default"]["src-layout"] = False + config_file.model.template.licenses.default = ["MIT", "Apache-2.0"] config_file.save() with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' + project_path = temp_dir / "my-app" # Ensure that we trigger the non-file case for code coverage - (project_path / 'LICENSES' / 'test').mkdir() + (project_path / "LICENSES" / "test").mkdir() config = { - 'project': {'name': project_name, 'dynamic': ['version'], 'license-files': ['LICENSES/*']}, - 'tool': { - 'hatch': { - 'version': {'path': 'my_app/__about__.py'}, - 'build': {'targets': {'wheel': {'versions': ['standard']}}}, + "project": {"name": project_name, "dynamic": ["version"], "license-files": ["LICENSES/*"]}, + "tool": { + "hatch": { + "version": {"path": "my_app/__about__.py"}, + "build": {"targets": {"wheel": {"versions": ["standard"]}}}, }, }, } builder = WheelBuilder(str(project_path), config=config) - build_path = project_path / 'dist' + build_path = project_path / "dist" build_path.mkdir() with project_path.as_cwd(): @@ -931,45 +931,45 @@ def test_default_multiple_licenses(self, hatch, helpers, config_file, temp_dir): build_artifacts = list(build_path.iterdir()) assert len(build_artifacts) == 1 assert expected_artifact == str(build_artifacts[0]) - assert expected_artifact == str(build_path / f'{builder.project_id}-{get_python_versions_tag()}-none-any.whl') + assert expected_artifact == str(build_path / f"{builder.project_id}-{get_python_versions_tag()}-none-any.whl") - extraction_directory = temp_dir / '_archive' + extraction_directory = temp_dir / "_archive" extraction_directory.mkdir() - with zipfile.ZipFile(str(expected_artifact), 'r') as zip_archive: + with zipfile.ZipFile(str(expected_artifact), "r") as zip_archive: zip_archive.extractall(str(extraction_directory)) - metadata_directory = f'{builder.project_id}.dist-info' + metadata_directory = f"{builder.project_id}.dist-info" expected_files = helpers.get_template_files( - 'wheel.standard_default_license_multiple', project_name, metadata_directory=metadata_directory + "wheel.standard_default_license_multiple", project_name, metadata_directory=metadata_directory ) helpers.assert_files(extraction_directory, expected_files) def test_default_include(self, hatch, helpers, temp_dir, config_file): - config_file.model.template.plugins['default']['src-layout'] = False + config_file.model.template.plugins["default"]["src-layout"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' + project_path = temp_dir / "my-app" config = { - 'project': {'name': project_name, 'dynamic': ['version']}, - 'tool': { - 'hatch': { - 'version': {'path': 'my_app/__about__.py'}, - 'build': {'targets': {'wheel': {'versions': ['standard'], 'include': ['my_app', 'tests']}}}, + "project": {"name": project_name, "dynamic": ["version"]}, + "tool": { + "hatch": { + "version": {"path": "my_app/__about__.py"}, + "build": {"targets": {"wheel": {"versions": ["standard"], "include": ["my_app", "tests"]}}}, }, }, } builder = WheelBuilder(str(project_path), config=config) - build_path = project_path / 'dist' + build_path = project_path / "dist" build_path.mkdir() with project_path.as_cwd(): @@ -981,43 +981,43 @@ def test_default_include(self, hatch, helpers, temp_dir, config_file): build_artifacts = list(build_path.iterdir()) assert len(build_artifacts) == 1 assert expected_artifact == str(build_artifacts[0]) - assert expected_artifact == str(build_path / f'{builder.project_id}-{get_python_versions_tag()}-none-any.whl') + assert expected_artifact == str(build_path / f"{builder.project_id}-{get_python_versions_tag()}-none-any.whl") - extraction_directory = temp_dir / '_archive' + extraction_directory = temp_dir / "_archive" extraction_directory.mkdir() - with zipfile.ZipFile(str(expected_artifact), 'r') as zip_archive: + with zipfile.ZipFile(str(expected_artifact), "r") as zip_archive: zip_archive.extractall(str(extraction_directory)) - metadata_directory = f'{builder.project_id}.dist-info' + metadata_directory = f"{builder.project_id}.dist-info" expected_files = helpers.get_template_files( - 'wheel.standard_tests', project_name, metadata_directory=metadata_directory + "wheel.standard_tests", project_name, metadata_directory=metadata_directory ) helpers.assert_files(extraction_directory, expected_files) def test_default_only_packages(self, hatch, helpers, temp_dir, config_file): - config_file.model.template.plugins['default']['src-layout'] = False + config_file.model.template.plugins["default"]["src-layout"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - tests_path = project_path / 'tests' - (tests_path / '__init__.py').replace(tests_path / 'foo.py') + project_path = temp_dir / "my-app" + tests_path = project_path / "tests" + (tests_path / "__init__.py").replace(tests_path / "foo.py") config = { - 'project': {'name': project_name, 'dynamic': ['version']}, - 'tool': { - 'hatch': { - 'version': {'path': 'my_app/__about__.py'}, - 'build': { - 'targets': { - 'wheel': {'versions': ['standard'], 'include': ['my_app', 'tests'], 'only-packages': True} + "project": {"name": project_name, "dynamic": ["version"]}, + "tool": { + "hatch": { + "version": {"path": "my_app/__about__.py"}, + "build": { + "targets": { + "wheel": {"versions": ["standard"], "include": ["my_app", "tests"], "only-packages": True} }, }, }, @@ -1025,7 +1025,7 @@ def test_default_only_packages(self, hatch, helpers, temp_dir, config_file): } builder = WheelBuilder(str(project_path), config=config) - build_path = project_path / 'dist' + build_path = project_path / "dist" build_path.mkdir() with project_path.as_cwd(): @@ -1037,44 +1037,44 @@ def test_default_only_packages(self, hatch, helpers, temp_dir, config_file): build_artifacts = list(build_path.iterdir()) assert len(build_artifacts) == 1 assert expected_artifact == str(build_artifacts[0]) - assert expected_artifact == str(build_path / f'{builder.project_id}-{get_python_versions_tag()}-none-any.whl') + assert expected_artifact == str(build_path / f"{builder.project_id}-{get_python_versions_tag()}-none-any.whl") - extraction_directory = temp_dir / '_archive' + extraction_directory = temp_dir / "_archive" extraction_directory.mkdir() - with zipfile.ZipFile(str(expected_artifact), 'r') as zip_archive: + with zipfile.ZipFile(str(expected_artifact), "r") as zip_archive: zip_archive.extractall(str(extraction_directory)) - metadata_directory = f'{builder.project_id}.dist-info' + metadata_directory = f"{builder.project_id}.dist-info" expected_files = helpers.get_template_files( - 'wheel.standard_default_license_single', project_name, metadata_directory=metadata_directory + "wheel.standard_default_license_single", project_name, metadata_directory=metadata_directory ) helpers.assert_files(extraction_directory, expected_files) def test_default_only_packages_artifact_override(self, hatch, helpers, temp_dir, config_file): - config_file.model.template.plugins['default']['src-layout'] = False + config_file.model.template.plugins["default"]["src-layout"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - tests_path = project_path / 'tests' - (tests_path / '__init__.py').replace(tests_path / 'foo.py') + project_path = temp_dir / "my-app" + tests_path = project_path / "tests" + (tests_path / "__init__.py").replace(tests_path / "foo.py") config = { - 'project': {'name': project_name, 'dynamic': ['version']}, - 'tool': { - 'hatch': { - 'version': {'path': 'my_app/__about__.py'}, - 'build': { - 'artifacts': ['foo.py'], - 'targets': { - 'wheel': {'versions': ['standard'], 'include': ['my_app', 'tests'], 'only-packages': True} + "project": {"name": project_name, "dynamic": ["version"]}, + "tool": { + "hatch": { + "version": {"path": "my_app/__about__.py"}, + "build": { + "artifacts": ["foo.py"], + "targets": { + "wheel": {"versions": ["standard"], "include": ["my_app", "tests"], "only-packages": True} }, }, }, @@ -1082,7 +1082,7 @@ def test_default_only_packages_artifact_override(self, hatch, helpers, temp_dir, } builder = WheelBuilder(str(project_path), config=config) - build_path = project_path / 'dist' + build_path = project_path / "dist" build_path.mkdir() with project_path.as_cwd(): @@ -1094,53 +1094,53 @@ def test_default_only_packages_artifact_override(self, hatch, helpers, temp_dir, build_artifacts = list(build_path.iterdir()) assert len(build_artifacts) == 1 assert expected_artifact == str(build_artifacts[0]) - assert expected_artifact == str(build_path / f'{builder.project_id}-{get_python_versions_tag()}-none-any.whl') + assert expected_artifact == str(build_path / f"{builder.project_id}-{get_python_versions_tag()}-none-any.whl") - extraction_directory = temp_dir / '_archive' + extraction_directory = temp_dir / "_archive" extraction_directory.mkdir() - with zipfile.ZipFile(str(expected_artifact), 'r') as zip_archive: + with zipfile.ZipFile(str(expected_artifact), "r") as zip_archive: zip_archive.extractall(str(extraction_directory)) - metadata_directory = f'{builder.project_id}.dist-info' + metadata_directory = f"{builder.project_id}.dist-info" expected_files = helpers.get_template_files( - 'wheel.standard_only_packages_artifact_override', project_name, metadata_directory=metadata_directory + "wheel.standard_only_packages_artifact_override", project_name, metadata_directory=metadata_directory ) helpers.assert_files(extraction_directory, expected_files) @pytest.mark.parametrize( - ('python_constraint', 'expected_template_file'), + ("python_constraint", "expected_template_file"), [ - pytest.param('>3', 'wheel.standard_default_python_constraint', id='>3'), - pytest.param('==3.11.4', 'wheel.standard_default_python_constraint_three_components', id='==3.11.4'), + pytest.param(">3", "wheel.standard_default_python_constraint", id=">3"), + pytest.param("==3.11.4", "wheel.standard_default_python_constraint_three_components", id="==3.11.4"), ], ) def test_default_python_constraint( self, hatch, helpers, temp_dir, config_file, python_constraint, expected_template_file ): - config_file.model.template.plugins['default']['src-layout'] = False + config_file.model.template.plugins["default"]["src-layout"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' + project_path = temp_dir / "my-app" config = { - 'project': {'name': project_name, 'requires-python': python_constraint, 'dynamic': ['version']}, - 'tool': { - 'hatch': { - 'version': {'path': 'my_app/__about__.py'}, - 'build': {'targets': {'wheel': {'versions': ['standard']}}}, + "project": {"name": project_name, "requires-python": python_constraint, "dynamic": ["version"]}, + "tool": { + "hatch": { + "version": {"path": "my_app/__about__.py"}, + "build": {"targets": {"wheel": {"versions": ["standard"]}}}, }, }, } builder = WheelBuilder(str(project_path), config=config) - build_path = project_path / 'dist' + build_path = project_path / "dist" build_path.mkdir() with project_path.as_cwd(): @@ -1152,32 +1152,32 @@ def test_default_python_constraint( build_artifacts = list(build_path.iterdir()) assert len(build_artifacts) == 1 assert expected_artifact == str(build_artifacts[0]) - assert expected_artifact == str(build_path / f'{builder.project_id}-py3-none-any.whl') + assert expected_artifact == str(build_path / f"{builder.project_id}-py3-none-any.whl") - extraction_directory = temp_dir / '_archive' + extraction_directory = temp_dir / "_archive" extraction_directory.mkdir() - with zipfile.ZipFile(str(expected_artifact), 'r') as zip_archive: + with zipfile.ZipFile(str(expected_artifact), "r") as zip_archive: zip_archive.extractall(str(extraction_directory)) - metadata_directory = f'{builder.project_id}.dist-info' + metadata_directory = f"{builder.project_id}.dist-info" expected_files = helpers.get_template_files( expected_template_file, project_name, metadata_directory=metadata_directory ) helpers.assert_files(extraction_directory, expected_files) def test_default_build_script_default_tag(self, hatch, helpers, temp_dir, config_file): - config_file.model.template.plugins['default']['src-layout'] = False + config_file.model.template.plugins["default"]["src-layout"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' + project_path = temp_dir / "my-app" build_script = project_path / DEFAULT_BUILD_SCRIPT build_script.write_text( @@ -1192,20 +1192,20 @@ class CustomHook(BuildHookInterface): ) config = { - 'project': {'name': project_name, 'requires-python': '>3', 'dynamic': ['version']}, - 'tool': { - 'hatch': { - 'version': {'path': 'my_app/__about__.py'}, - 'build': { - 'targets': {'wheel': {'versions': ['standard']}}, - 'hooks': {'custom': {'path': DEFAULT_BUILD_SCRIPT}}, + "project": {"name": project_name, "requires-python": ">3", "dynamic": ["version"]}, + "tool": { + "hatch": { + "version": {"path": "my_app/__about__.py"}, + "build": { + "targets": {"wheel": {"versions": ["standard"]}}, + "hooks": {"custom": {"path": DEFAULT_BUILD_SCRIPT}}, }, }, }, } builder = WheelBuilder(str(project_path), config=config) - build_path = project_path / 'dist' + build_path = project_path / "dist" build_path.mkdir() with project_path.as_cwd(): @@ -1218,33 +1218,33 @@ class CustomHook(BuildHookInterface): assert len(build_artifacts) == 1 assert expected_artifact == str(build_artifacts[0]) - tag = 'py3-none-any' - assert expected_artifact == str(build_path / f'{builder.project_id}-{tag}.whl') + tag = "py3-none-any" + assert expected_artifact == str(build_path / f"{builder.project_id}-{tag}.whl") - extraction_directory = temp_dir / '_archive' + extraction_directory = temp_dir / "_archive" extraction_directory.mkdir() - with zipfile.ZipFile(str(expected_artifact), 'r') as zip_archive: + with zipfile.ZipFile(str(expected_artifact), "r") as zip_archive: zip_archive.extractall(str(extraction_directory)) - metadata_directory = f'{builder.project_id}.dist-info' + metadata_directory = f"{builder.project_id}.dist-info" expected_files = helpers.get_template_files( - 'wheel.standard_default_build_script', project_name, metadata_directory=metadata_directory, tag=tag + "wheel.standard_default_build_script", project_name, metadata_directory=metadata_directory, tag=tag ) helpers.assert_files(extraction_directory, expected_files) def test_default_build_script_set_tag(self, hatch, helpers, temp_dir, config_file): - config_file.model.template.plugins['default']['src-layout'] = False + config_file.model.template.plugins["default"]["src-layout"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' + project_path = temp_dir / "my-app" build_script = project_path / DEFAULT_BUILD_SCRIPT build_script.write_text( @@ -1260,20 +1260,20 @@ def initialize(self, version, build_data): ) config = { - 'project': {'name': project_name, 'requires-python': '>3', 'dynamic': ['version']}, - 'tool': { - 'hatch': { - 'version': {'path': 'my_app/__about__.py'}, - 'build': { - 'targets': {'wheel': {'versions': ['standard']}}, - 'hooks': {'custom': {'path': DEFAULT_BUILD_SCRIPT}}, + "project": {"name": project_name, "requires-python": ">3", "dynamic": ["version"]}, + "tool": { + "hatch": { + "version": {"path": "my_app/__about__.py"}, + "build": { + "targets": {"wheel": {"versions": ["standard"]}}, + "hooks": {"custom": {"path": DEFAULT_BUILD_SCRIPT}}, }, }, }, } builder = WheelBuilder(str(project_path), config=config) - build_path = project_path / 'dist' + build_path = project_path / "dist" build_path.mkdir() with project_path.as_cwd(): @@ -1286,36 +1286,36 @@ def initialize(self, version, build_data): assert len(build_artifacts) == 1 assert expected_artifact == str(build_artifacts[0]) - tag = 'foo-bar-baz' - assert expected_artifact == str(build_path / f'{builder.project_id}-{tag}.whl') + tag = "foo-bar-baz" + assert expected_artifact == str(build_path / f"{builder.project_id}-{tag}.whl") - extraction_directory = temp_dir / '_archive' + extraction_directory = temp_dir / "_archive" extraction_directory.mkdir() - with zipfile.ZipFile(str(expected_artifact), 'r') as zip_archive: + with zipfile.ZipFile(str(expected_artifact), "r") as zip_archive: zip_archive.extractall(str(extraction_directory)) - metadata_directory = f'{builder.project_id}.dist-info' + metadata_directory = f"{builder.project_id}.dist-info" expected_files = helpers.get_template_files( - 'wheel.standard_default_build_script', project_name, metadata_directory=metadata_directory, tag=tag + "wheel.standard_default_build_script", project_name, metadata_directory=metadata_directory, tag=tag ) helpers.assert_files(extraction_directory, expected_files) def test_default_build_script_known_artifacts(self, hatch, helpers, temp_dir, config_file): - config_file.model.template.plugins['default']['src-layout'] = False + config_file.model.template.plugins["default"]["src-layout"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' + project_path = temp_dir / "my-app" - vcs_ignore_file = project_path / '.gitignore' - vcs_ignore_file.write_text('*.pyc\n*.so\n*.h') + vcs_ignore_file = project_path / ".gitignore" + vcs_ignore_file.write_text("*.pyc\n*.so\n*.h") build_script = project_path / DEFAULT_BUILD_SCRIPT build_script.write_text( @@ -1337,21 +1337,21 @@ def initialize(self, version, build_data): ) config = { - 'project': {'name': project_name, 'requires-python': '>3', 'dynamic': ['version']}, - 'tool': { - 'hatch': { - 'version': {'path': 'my_app/__about__.py'}, - 'build': { - 'targets': {'wheel': {'versions': ['standard']}}, - 'artifacts': ['my_app/lib.so'], - 'hooks': {'custom': {'path': DEFAULT_BUILD_SCRIPT}}, + "project": {"name": project_name, "requires-python": ">3", "dynamic": ["version"]}, + "tool": { + "hatch": { + "version": {"path": "my_app/__about__.py"}, + "build": { + "targets": {"wheel": {"versions": ["standard"]}}, + "artifacts": ["my_app/lib.so"], + "hooks": {"custom": {"path": DEFAULT_BUILD_SCRIPT}}, }, }, }, } builder = WheelBuilder(str(project_path), config=config) - build_path = project_path / 'dist' + build_path = project_path / "dist" build_path.mkdir() with project_path.as_cwd(): @@ -1365,18 +1365,18 @@ def initialize(self, version, build_data): assert expected_artifact == str(build_artifacts[0]) best_matching_tag = next(sys_tags()) - tag = f'{best_matching_tag.interpreter}-{best_matching_tag.abi}-{best_matching_tag.platform}' - assert expected_artifact == str(build_path / f'{builder.project_id}-{tag}.whl') + tag = f"{best_matching_tag.interpreter}-{best_matching_tag.abi}-{best_matching_tag.platform}" + assert expected_artifact == str(build_path / f"{builder.project_id}-{tag}.whl") - extraction_directory = temp_dir / '_archive' + extraction_directory = temp_dir / "_archive" extraction_directory.mkdir() - with zipfile.ZipFile(str(expected_artifact), 'r') as zip_archive: + with zipfile.ZipFile(str(expected_artifact), "r") as zip_archive: zip_archive.extractall(str(extraction_directory)) - metadata_directory = f'{builder.project_id}.dist-info' + metadata_directory = f"{builder.project_id}.dist-info" expected_files = helpers.get_template_files( - 'wheel.standard_default_build_script_artifacts', + "wheel.standard_default_build_script_artifacts", project_name, metadata_directory=metadata_directory, tag=tag, @@ -1384,20 +1384,20 @@ def initialize(self, version, build_data): helpers.assert_files(extraction_directory, expected_files) def test_default_build_script_configured_build_hooks(self, hatch, helpers, temp_dir, config_file): - config_file.model.template.plugins['default']['src-layout'] = False + config_file.model.template.plugins["default"]["src-layout"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' + project_path = temp_dir / "my-app" - vcs_ignore_file = project_path / '.gitignore' - vcs_ignore_file.write_text('*.pyc\n*.so\n*.h') + vcs_ignore_file = project_path / ".gitignore" + vcs_ignore_file.write_text("*.pyc\n*.so\n*.h") build_script = project_path / DEFAULT_BUILD_SCRIPT build_script.write_text( @@ -1419,21 +1419,21 @@ def initialize(self, version, build_data): ) config = { - 'project': {'name': project_name, 'requires-python': '>3', 'dynamic': ['version']}, - 'tool': { - 'hatch': { - 'version': {'path': 'my_app/__about__.py'}, - 'build': { - 'targets': {'wheel': {'versions': ['standard']}}, - 'artifacts': ['my_app/lib.so'], - 'hooks': {'custom': {'path': DEFAULT_BUILD_SCRIPT}}, + "project": {"name": project_name, "requires-python": ">3", "dynamic": ["version"]}, + "tool": { + "hatch": { + "version": {"path": "my_app/__about__.py"}, + "build": { + "targets": {"wheel": {"versions": ["standard"]}}, + "artifacts": ["my_app/lib.so"], + "hooks": {"custom": {"path": DEFAULT_BUILD_SCRIPT}}, }, }, }, } builder = WheelBuilder(str(project_path), config=config) - build_path = project_path / 'dist' + build_path = project_path / "dist" build_path.mkdir() with project_path.as_cwd(): @@ -1447,18 +1447,18 @@ def initialize(self, version, build_data): assert expected_artifact == str(build_artifacts[0]) best_matching_tag = next(sys_tags()) - tag = f'{best_matching_tag.interpreter}-{best_matching_tag.abi}-{best_matching_tag.platform}' - assert expected_artifact == str(build_path / f'{builder.project_id}-{tag}.whl') + tag = f"{best_matching_tag.interpreter}-{best_matching_tag.abi}-{best_matching_tag.platform}" + assert expected_artifact == str(build_path / f"{builder.project_id}-{tag}.whl") - extraction_directory = temp_dir / '_archive' + extraction_directory = temp_dir / "_archive" extraction_directory.mkdir() - with zipfile.ZipFile(str(expected_artifact), 'r') as zip_archive: + with zipfile.ZipFile(str(expected_artifact), "r") as zip_archive: zip_archive.extractall(str(extraction_directory)) - metadata_directory = f'{builder.project_id}.dist-info' + metadata_directory = f"{builder.project_id}.dist-info" expected_files = helpers.get_template_files( - 'wheel.standard_default_build_script_configured_build_hooks', + "wheel.standard_default_build_script_configured_build_hooks", project_name, metadata_directory=metadata_directory, tag=tag, @@ -1466,20 +1466,20 @@ def initialize(self, version, build_data): helpers.assert_files(extraction_directory, expected_files) def test_default_build_script_extra_dependencies(self, hatch, helpers, temp_dir, config_file): - config_file.model.template.plugins['default']['src-layout'] = False + config_file.model.template.plugins["default"]["src-layout"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' + project_path = temp_dir / "my-app" - vcs_ignore_file = project_path / '.gitignore' - vcs_ignore_file.write_text('*.pyc\n*.so\n*.h') + vcs_ignore_file = project_path / ".gitignore" + vcs_ignore_file.write_text("*.pyc\n*.so\n*.h") build_script = project_path / DEFAULT_BUILD_SCRIPT build_script.write_text( @@ -1502,21 +1502,21 @@ def initialize(self, version, build_data): ) config = { - 'project': {'name': project_name, 'requires-python': '>3', 'dynamic': ['version']}, - 'tool': { - 'hatch': { - 'version': {'path': 'my_app/__about__.py'}, - 'build': { - 'targets': {'wheel': {'versions': ['standard']}}, - 'artifacts': ['my_app/lib.so'], - 'hooks': {'custom': {'path': DEFAULT_BUILD_SCRIPT}}, + "project": {"name": project_name, "requires-python": ">3", "dynamic": ["version"]}, + "tool": { + "hatch": { + "version": {"path": "my_app/__about__.py"}, + "build": { + "targets": {"wheel": {"versions": ["standard"]}}, + "artifacts": ["my_app/lib.so"], + "hooks": {"custom": {"path": DEFAULT_BUILD_SCRIPT}}, }, }, }, } builder = WheelBuilder(str(project_path), config=config) - build_path = project_path / 'dist' + build_path = project_path / "dist" build_path.mkdir() with project_path.as_cwd(): @@ -1530,18 +1530,18 @@ def initialize(self, version, build_data): assert expected_artifact == str(build_artifacts[0]) best_matching_tag = next(sys_tags()) - tag = f'{best_matching_tag.interpreter}-{best_matching_tag.abi}-{best_matching_tag.platform}' - assert expected_artifact == str(build_path / f'{builder.project_id}-{tag}.whl') + tag = f"{best_matching_tag.interpreter}-{best_matching_tag.abi}-{best_matching_tag.platform}" + assert expected_artifact == str(build_path / f"{builder.project_id}-{tag}.whl") - extraction_directory = temp_dir / '_archive' + extraction_directory = temp_dir / "_archive" extraction_directory.mkdir() - with zipfile.ZipFile(str(expected_artifact), 'r') as zip_archive: + with zipfile.ZipFile(str(expected_artifact), "r") as zip_archive: zip_archive.extractall(str(extraction_directory)) - metadata_directory = f'{builder.project_id}.dist-info' + metadata_directory = f"{builder.project_id}.dist-info" expected_files = helpers.get_template_files( - 'wheel.standard_default_build_script_extra_dependencies', + "wheel.standard_default_build_script_extra_dependencies", project_name, metadata_directory=metadata_directory, tag=tag, @@ -1549,20 +1549,20 @@ def initialize(self, version, build_data): helpers.assert_files(extraction_directory, expected_files) def test_default_build_script_dynamic_artifacts(self, hatch, helpers, temp_dir, config_file): - config_file.model.template.plugins['default']['src-layout'] = False + config_file.model.template.plugins["default"]["src-layout"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' + project_path = temp_dir / "my-app" - vcs_ignore_file = project_path / '.gitignore' - vcs_ignore_file.write_text('*.pyc\n*.so\n*.h') + vcs_ignore_file = project_path / ".gitignore" + vcs_ignore_file.write_text("*.pyc\n*.so\n*.h") build_script = project_path / DEFAULT_BUILD_SCRIPT build_script.write_text( @@ -1585,20 +1585,20 @@ def initialize(self, version, build_data): ) config = { - 'project': {'name': project_name, 'requires-python': '>3', 'dynamic': ['version']}, - 'tool': { - 'hatch': { - 'version': {'path': 'my_app/__about__.py'}, - 'build': { - 'targets': {'wheel': {'versions': ['standard']}}, - 'hooks': {'custom': {'path': DEFAULT_BUILD_SCRIPT}}, + "project": {"name": project_name, "requires-python": ">3", "dynamic": ["version"]}, + "tool": { + "hatch": { + "version": {"path": "my_app/__about__.py"}, + "build": { + "targets": {"wheel": {"versions": ["standard"]}}, + "hooks": {"custom": {"path": DEFAULT_BUILD_SCRIPT}}, }, }, }, } builder = WheelBuilder(str(project_path), config=config) - build_path = project_path / 'dist' + build_path = project_path / "dist" build_path.mkdir() with project_path.as_cwd(): @@ -1612,18 +1612,18 @@ def initialize(self, version, build_data): assert expected_artifact == str(build_artifacts[0]) best_matching_tag = next(sys_tags()) - tag = f'{best_matching_tag.interpreter}-{best_matching_tag.abi}-{best_matching_tag.platform}' - assert expected_artifact == str(build_path / f'{builder.project_id}-{tag}.whl') + tag = f"{best_matching_tag.interpreter}-{best_matching_tag.abi}-{best_matching_tag.platform}" + assert expected_artifact == str(build_path / f"{builder.project_id}-{tag}.whl") - extraction_directory = temp_dir / '_archive' + extraction_directory = temp_dir / "_archive" extraction_directory.mkdir() - with zipfile.ZipFile(str(expected_artifact), 'r') as zip_archive: + with zipfile.ZipFile(str(expected_artifact), "r") as zip_archive: zip_archive.extractall(str(extraction_directory)) - metadata_directory = f'{builder.project_id}.dist-info' + metadata_directory = f"{builder.project_id}.dist-info" expected_files = helpers.get_template_files( - 'wheel.standard_default_build_script_artifacts', + "wheel.standard_default_build_script_artifacts", project_name, metadata_directory=metadata_directory, tag=tag, @@ -1631,20 +1631,20 @@ def initialize(self, version, build_data): helpers.assert_files(extraction_directory, expected_files) def test_default_build_script_dynamic_force_include(self, hatch, helpers, temp_dir, config_file): - config_file.model.template.plugins['default']['src-layout'] = False + config_file.model.template.plugins["default"]["src-layout"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' + project_path = temp_dir / "my-app" - vcs_ignore_file = project_path / '.gitignore' - vcs_ignore_file.write_text('*.pyc\n*.so\n*.h') + vcs_ignore_file = project_path / ".gitignore" + vcs_ignore_file.write_text("*.pyc\n*.so\n*.h") build_script = project_path / DEFAULT_BUILD_SCRIPT build_script.write_text( @@ -1670,20 +1670,20 @@ def initialize(self, version, build_data): ) config = { - 'project': {'name': project_name, 'requires-python': '>3', 'dynamic': ['version']}, - 'tool': { - 'hatch': { - 'version': {'path': 'my_app/__about__.py'}, - 'build': { - 'targets': {'wheel': {'versions': ['standard']}}, - 'hooks': {'custom': {'path': DEFAULT_BUILD_SCRIPT}}, + "project": {"name": project_name, "requires-python": ">3", "dynamic": ["version"]}, + "tool": { + "hatch": { + "version": {"path": "my_app/__about__.py"}, + "build": { + "targets": {"wheel": {"versions": ["standard"]}}, + "hooks": {"custom": {"path": DEFAULT_BUILD_SCRIPT}}, }, }, }, } builder = WheelBuilder(str(project_path), config=config) - build_path = project_path / 'dist' + build_path = project_path / "dist" build_path.mkdir() with project_path.as_cwd(): @@ -1697,18 +1697,18 @@ def initialize(self, version, build_data): assert expected_artifact == str(build_artifacts[0]) best_matching_tag = next(sys_tags()) - tag = f'{best_matching_tag.interpreter}-{best_matching_tag.abi}-{best_matching_tag.platform}' - assert expected_artifact == str(build_path / f'{builder.project_id}-{tag}.whl') + tag = f"{best_matching_tag.interpreter}-{best_matching_tag.abi}-{best_matching_tag.platform}" + assert expected_artifact == str(build_path / f"{builder.project_id}-{tag}.whl") - extraction_directory = temp_dir / '_archive' + extraction_directory = temp_dir / "_archive" extraction_directory.mkdir() - with zipfile.ZipFile(str(expected_artifact), 'r') as zip_archive: + with zipfile.ZipFile(str(expected_artifact), "r") as zip_archive: zip_archive.extractall(str(extraction_directory)) - metadata_directory = f'{builder.project_id}.dist-info' + metadata_directory = f"{builder.project_id}.dist-info" expected_files = helpers.get_template_files( - 'wheel.standard_default_build_script_force_include', + "wheel.standard_default_build_script_force_include", project_name, metadata_directory=metadata_directory, tag=tag, @@ -1716,22 +1716,22 @@ def initialize(self, version, build_data): helpers.assert_files(extraction_directory, expected_files) def test_default_build_script_dynamic_force_include_duplicate(self, hatch, helpers, temp_dir, config_file): - config_file.model.template.plugins['default']['src-layout'] = False + config_file.model.template.plugins["default"]["src-layout"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' + project_path = temp_dir / "my-app" - vcs_ignore_file = project_path / '.gitignore' - vcs_ignore_file.write_text('*.pyc\n*.so\n*.h') + vcs_ignore_file = project_path / ".gitignore" + vcs_ignore_file.write_text("*.pyc\n*.so\n*.h") - target_file = project_path / 'my_app' / 'z.py' + target_file = project_path / "my_app" / "z.py" target_file.write_text('print("hello world")') build_script = project_path / DEFAULT_BUILD_SCRIPT @@ -1756,20 +1756,20 @@ def initialize(self, version, build_data): ) config = { - 'project': {'name': project_name, 'requires-python': '>3', 'dynamic': ['version']}, - 'tool': { - 'hatch': { - 'version': {'path': 'my_app/__about__.py'}, - 'build': { - 'targets': {'wheel': {'versions': ['standard']}}, - 'hooks': {'custom': {'path': DEFAULT_BUILD_SCRIPT}}, + "project": {"name": project_name, "requires-python": ">3", "dynamic": ["version"]}, + "tool": { + "hatch": { + "version": {"path": "my_app/__about__.py"}, + "build": { + "targets": {"wheel": {"versions": ["standard"]}}, + "hooks": {"custom": {"path": DEFAULT_BUILD_SCRIPT}}, }, }, }, } builder = WheelBuilder(str(project_path), config=config) - build_path = project_path / 'dist' + build_path = project_path / "dist" build_path.mkdir() with project_path.as_cwd(): @@ -1783,18 +1783,18 @@ def initialize(self, version, build_data): assert expected_artifact == str(build_artifacts[0]) best_matching_tag = next(sys_tags()) - tag = f'{best_matching_tag.interpreter}-{best_matching_tag.abi}-{best_matching_tag.platform}' - assert expected_artifact == str(build_path / f'{builder.project_id}-{tag}.whl') + tag = f"{best_matching_tag.interpreter}-{best_matching_tag.abi}-{best_matching_tag.platform}" + assert expected_artifact == str(build_path / f"{builder.project_id}-{tag}.whl") - extraction_directory = temp_dir / '_archive' + extraction_directory = temp_dir / "_archive" extraction_directory.mkdir() - with zipfile.ZipFile(str(expected_artifact), 'r') as zip_archive: + with zipfile.ZipFile(str(expected_artifact), "r") as zip_archive: zip_archive.extractall(str(extraction_directory)) - metadata_directory = f'{builder.project_id}.dist-info' + metadata_directory = f"{builder.project_id}.dist-info" expected_files = helpers.get_template_files( - 'wheel.standard_default_build_script_force_include_no_duplication', + "wheel.standard_default_build_script_force_include_no_duplication", project_name, metadata_directory=metadata_directory, tag=tag, @@ -1802,17 +1802,17 @@ def initialize(self, version, build_data): helpers.assert_files(extraction_directory, expected_files) def test_default_build_script_dynamic_artifacts_with_src_layout(self, hatch, helpers, temp_dir): - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' + project_path = temp_dir / "my-app" - vcs_ignore_file = project_path / '.gitignore' - vcs_ignore_file.write_text('*.pyc\n*.so\n*.pyd\n*.h') + vcs_ignore_file = project_path / ".gitignore" + vcs_ignore_file.write_text("*.pyc\n*.so\n*.pyd\n*.h") build_script = project_path / DEFAULT_BUILD_SCRIPT build_script.write_text( @@ -1837,20 +1837,20 @@ def initialize(self, version, build_data): ) config = { - 'project': {'name': project_name, 'requires-python': '>3', 'dynamic': ['version']}, - 'tool': { - 'hatch': { - 'version': {'path': 'src/my_app/__about__.py'}, - 'build': { - 'targets': {'wheel': {'versions': ['standard']}}, - 'hooks': {'custom': {'path': DEFAULT_BUILD_SCRIPT}}, + "project": {"name": project_name, "requires-python": ">3", "dynamic": ["version"]}, + "tool": { + "hatch": { + "version": {"path": "src/my_app/__about__.py"}, + "build": { + "targets": {"wheel": {"versions": ["standard"]}}, + "hooks": {"custom": {"path": DEFAULT_BUILD_SCRIPT}}, }, }, }, } builder = WheelBuilder(str(project_path), config=config) - build_path = project_path / 'dist' + build_path = project_path / "dist" build_path.mkdir() with project_path.as_cwd(): @@ -1864,18 +1864,18 @@ def initialize(self, version, build_data): assert expected_artifact == str(build_artifacts[0]) best_matching_tag = next(sys_tags()) - tag = f'{best_matching_tag.interpreter}-{best_matching_tag.abi}-{best_matching_tag.platform}' - assert expected_artifact == str(build_path / f'{builder.project_id}-{tag}.whl') + tag = f"{best_matching_tag.interpreter}-{best_matching_tag.abi}-{best_matching_tag.platform}" + assert expected_artifact == str(build_path / f"{builder.project_id}-{tag}.whl") - extraction_directory = temp_dir / '_archive' + extraction_directory = temp_dir / "_archive" extraction_directory.mkdir() - with zipfile.ZipFile(str(expected_artifact), 'r') as zip_archive: + with zipfile.ZipFile(str(expected_artifact), "r") as zip_archive: zip_archive.extractall(str(extraction_directory)) - metadata_directory = f'{builder.project_id}.dist-info' + metadata_directory = f"{builder.project_id}.dist-info" expected_files = helpers.get_template_files( - 'wheel.standard_default_build_script_artifacts_with_src_layout', + "wheel.standard_default_build_script_artifacts_with_src_layout", project_name, metadata_directory=metadata_directory, tag=tag, @@ -1883,37 +1883,37 @@ def initialize(self, version, build_data): helpers.assert_files(extraction_directory, expected_files) def test_default_shared_data(self, hatch, helpers, temp_dir, config_file): - config_file.model.template.plugins['default']['src-layout'] = False + config_file.model.template.plugins["default"]["src-layout"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' + project_path = temp_dir / "my-app" - shared_data_path = temp_dir / 'data' + shared_data_path = temp_dir / "data" shared_data_path.ensure_dir_exists() - (shared_data_path / 'foo.txt').touch() - nested_data_path = shared_data_path / 'nested' + (shared_data_path / "foo.txt").touch() + nested_data_path = shared_data_path / "nested" nested_data_path.ensure_dir_exists() - (nested_data_path / 'bar.txt').touch() + (nested_data_path / "bar.txt").touch() config = { - 'project': {'name': project_name, 'requires-python': '>3', 'dynamic': ['version']}, - 'tool': { - 'hatch': { - 'version': {'path': 'my_app/__about__.py'}, - 'build': {'targets': {'wheel': {'versions': ['standard'], 'shared-data': {'../data': '/'}}}}, + "project": {"name": project_name, "requires-python": ">3", "dynamic": ["version"]}, + "tool": { + "hatch": { + "version": {"path": "my_app/__about__.py"}, + "build": {"targets": {"wheel": {"versions": ["standard"], "shared-data": {"../data": "/"}}}}, }, }, } builder = WheelBuilder(str(project_path), config=config) - build_path = project_path / 'dist' + build_path = project_path / "dist" build_path.mkdir() with project_path.as_cwd(): @@ -1926,16 +1926,16 @@ def test_default_shared_data(self, hatch, helpers, temp_dir, config_file): assert len(build_artifacts) == 1 assert expected_artifact == str(build_artifacts[0]) - extraction_directory = temp_dir / '_archive' + extraction_directory = temp_dir / "_archive" extraction_directory.mkdir() - with zipfile.ZipFile(str(expected_artifact), 'r') as zip_archive: + with zipfile.ZipFile(str(expected_artifact), "r") as zip_archive: zip_archive.extractall(str(extraction_directory)) - metadata_directory = f'{builder.project_id}.dist-info' - shared_data_directory = f'{builder.project_id}.data' + metadata_directory = f"{builder.project_id}.dist-info" + shared_data_directory = f"{builder.project_id}.data" expected_files = helpers.get_template_files( - 'wheel.standard_default_shared_data', + "wheel.standard_default_shared_data", project_name, metadata_directory=metadata_directory, shared_data_directory=shared_data_directory, @@ -1943,24 +1943,24 @@ def test_default_shared_data(self, hatch, helpers, temp_dir, config_file): helpers.assert_files(extraction_directory, expected_files) def test_default_shared_data_from_build_data(self, hatch, helpers, temp_dir, config_file): - config_file.model.template.plugins['default']['src-layout'] = False + config_file.model.template.plugins["default"]["src-layout"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' + project_path = temp_dir / "my-app" - shared_data_path = temp_dir / 'data' + shared_data_path = temp_dir / "data" shared_data_path.ensure_dir_exists() - (shared_data_path / 'foo.txt').touch() - nested_data_path = shared_data_path / 'nested' + (shared_data_path / "foo.txt").touch() + nested_data_path = shared_data_path / "nested" nested_data_path.ensure_dir_exists() - (nested_data_path / 'bar.txt').touch() + (nested_data_path / "bar.txt").touch() build_script = project_path / DEFAULT_BUILD_SCRIPT build_script.write_text( @@ -1978,17 +1978,17 @@ def initialize(self, version, build_data): ) config = { - 'project': {'name': project_name, 'requires-python': '>3', 'dynamic': ['version']}, - 'tool': { - 'hatch': { - 'version': {'path': 'my_app/__about__.py'}, - 'build': {'targets': {'wheel': {'versions': ['standard'], 'hooks': {'custom': {}}}}}, + "project": {"name": project_name, "requires-python": ">3", "dynamic": ["version"]}, + "tool": { + "hatch": { + "version": {"path": "my_app/__about__.py"}, + "build": {"targets": {"wheel": {"versions": ["standard"], "hooks": {"custom": {}}}}}, }, }, } builder = WheelBuilder(str(project_path), config=config) - build_path = project_path / 'dist' + build_path = project_path / "dist" build_path.mkdir() with project_path.as_cwd(): @@ -2001,16 +2001,16 @@ def initialize(self, version, build_data): assert len(build_artifacts) == 1 assert expected_artifact == str(build_artifacts[0]) - extraction_directory = temp_dir / '_archive' + extraction_directory = temp_dir / "_archive" extraction_directory.mkdir() - with zipfile.ZipFile(str(expected_artifact), 'r') as zip_archive: + with zipfile.ZipFile(str(expected_artifact), "r") as zip_archive: zip_archive.extractall(str(extraction_directory)) - metadata_directory = f'{builder.project_id}.dist-info' - shared_data_directory = f'{builder.project_id}.data' + metadata_directory = f"{builder.project_id}.dist-info" + shared_data_directory = f"{builder.project_id}.data" expected_files = helpers.get_template_files( - 'wheel.standard_default_shared_data', + "wheel.standard_default_shared_data", project_name, metadata_directory=metadata_directory, shared_data_directory=shared_data_directory, @@ -2018,29 +2018,29 @@ def initialize(self, version, build_data): helpers.assert_files(extraction_directory, expected_files) def test_default_shared_scripts(self, hatch, platform, helpers, temp_dir, config_file): - config_file.model.template.plugins['default']['src-layout'] = False + config_file.model.template.plugins["default"]["src-layout"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' + project_path = temp_dir / "my-app" - shared_data_path = temp_dir / 'data' + shared_data_path = temp_dir / "data" shared_data_path.ensure_dir_exists() binary_contents = os.urandom(1024) - binary_file = shared_data_path / 'binary' + binary_file = shared_data_path / "binary" binary_file.write_bytes(binary_contents) if not platform.windows: expected_mode = 0o755 binary_file.chmod(expected_mode) - (shared_data_path / 'other_script.sh').write_text( + (shared_data_path / "other_script.sh").write_text( helpers.dedent( """ @@ -2049,7 +2049,7 @@ def test_default_shared_scripts(self, hatch, platform, helpers, temp_dir, config """ ) ) - (shared_data_path / 'python_script.sh').write_text( + (shared_data_path / "python_script.sh").write_text( helpers.dedent( """ @@ -2058,7 +2058,7 @@ def test_default_shared_scripts(self, hatch, platform, helpers, temp_dir, config """ ) ) - (shared_data_path / 'pythonw_script.sh').write_text( + (shared_data_path / "pythonw_script.sh").write_text( helpers.dedent( """ @@ -2067,7 +2067,7 @@ def test_default_shared_scripts(self, hatch, platform, helpers, temp_dir, config """ ) ) - (shared_data_path / 'pypy_script.sh').write_text( + (shared_data_path / "pypy_script.sh").write_text( helpers.dedent( """ @@ -2076,7 +2076,7 @@ def test_default_shared_scripts(self, hatch, platform, helpers, temp_dir, config """ ) ) - (shared_data_path / 'pypyw_script.sh').write_text( + (shared_data_path / "pypyw_script.sh").write_text( helpers.dedent( """ @@ -2087,17 +2087,17 @@ def test_default_shared_scripts(self, hatch, platform, helpers, temp_dir, config ) config = { - 'project': {'name': project_name, 'requires-python': '>3', 'dynamic': ['version']}, - 'tool': { - 'hatch': { - 'version': {'path': 'my_app/__about__.py'}, - 'build': {'targets': {'wheel': {'versions': ['standard'], 'shared-scripts': {'../data': '/'}}}}, + "project": {"name": project_name, "requires-python": ">3", "dynamic": ["version"]}, + "tool": { + "hatch": { + "version": {"path": "my_app/__about__.py"}, + "build": {"targets": {"wheel": {"versions": ["standard"], "shared-scripts": {"../data": "/"}}}}, }, }, } builder = WheelBuilder(str(project_path), config=config) - build_path = project_path / 'dist' + build_path = project_path / "dist" build_path.mkdir() with project_path.as_cwd(): @@ -2110,13 +2110,13 @@ def test_default_shared_scripts(self, hatch, platform, helpers, temp_dir, config assert len(build_artifacts) == 1 assert expected_artifact == str(build_artifacts[0]) - extraction_directory = temp_dir / '_archive' + extraction_directory = temp_dir / "_archive" extract_zip(expected_artifact, extraction_directory) - metadata_directory = f'{builder.project_id}.dist-info' - shared_data_directory = f'{builder.project_id}.data' + metadata_directory = f"{builder.project_id}.dist-info" + shared_data_directory = f"{builder.project_id}.data" expected_files = helpers.get_template_files( - 'wheel.standard_default_shared_scripts', + "wheel.standard_default_shared_scripts", project_name, metadata_directory=metadata_directory, shared_data_directory=shared_data_directory, @@ -2125,33 +2125,33 @@ def test_default_shared_scripts(self, hatch, platform, helpers, temp_dir, config helpers.assert_files(extraction_directory, expected_files) if not platform.windows: - extracted_binary = extraction_directory / shared_data_directory / 'scripts' / 'binary' + extracted_binary = extraction_directory / shared_data_directory / "scripts" / "binary" assert extracted_binary.stat().st_mode & 0o777 == expected_mode def test_default_shared_scripts_from_build_data(self, hatch, platform, helpers, temp_dir, config_file): - config_file.model.template.plugins['default']['src-layout'] = False + config_file.model.template.plugins["default"]["src-layout"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' + project_path = temp_dir / "my-app" - shared_data_path = temp_dir / 'data' + shared_data_path = temp_dir / "data" shared_data_path.ensure_dir_exists() binary_contents = os.urandom(1024) - binary_file = shared_data_path / 'binary' + binary_file = shared_data_path / "binary" binary_file.write_bytes(binary_contents) if not platform.windows: expected_mode = 0o755 binary_file.chmod(expected_mode) - (shared_data_path / 'other_script.sh').write_text( + (shared_data_path / "other_script.sh").write_text( helpers.dedent( """ @@ -2160,7 +2160,7 @@ def test_default_shared_scripts_from_build_data(self, hatch, platform, helpers, """ ) ) - (shared_data_path / 'python_script.sh').write_text( + (shared_data_path / "python_script.sh").write_text( helpers.dedent( """ @@ -2169,7 +2169,7 @@ def test_default_shared_scripts_from_build_data(self, hatch, platform, helpers, """ ) ) - (shared_data_path / 'pythonw_script.sh').write_text( + (shared_data_path / "pythonw_script.sh").write_text( helpers.dedent( """ @@ -2178,7 +2178,7 @@ def test_default_shared_scripts_from_build_data(self, hatch, platform, helpers, """ ) ) - (shared_data_path / 'pypy_script.sh').write_text( + (shared_data_path / "pypy_script.sh").write_text( helpers.dedent( """ @@ -2187,7 +2187,7 @@ def test_default_shared_scripts_from_build_data(self, hatch, platform, helpers, """ ) ) - (shared_data_path / 'pypyw_script.sh').write_text( + (shared_data_path / "pypyw_script.sh").write_text( helpers.dedent( """ @@ -2213,17 +2213,17 @@ def initialize(self, version, build_data): ) config = { - 'project': {'name': project_name, 'requires-python': '>3', 'dynamic': ['version']}, - 'tool': { - 'hatch': { - 'version': {'path': 'my_app/__about__.py'}, - 'build': {'targets': {'wheel': {'versions': ['standard'], 'hooks': {'custom': {}}}}}, + "project": {"name": project_name, "requires-python": ">3", "dynamic": ["version"]}, + "tool": { + "hatch": { + "version": {"path": "my_app/__about__.py"}, + "build": {"targets": {"wheel": {"versions": ["standard"], "hooks": {"custom": {}}}}}, }, }, } builder = WheelBuilder(str(project_path), config=config) - build_path = project_path / 'dist' + build_path = project_path / "dist" build_path.mkdir() with project_path.as_cwd(): @@ -2236,13 +2236,13 @@ def initialize(self, version, build_data): assert len(build_artifacts) == 1 assert expected_artifact == str(build_artifacts[0]) - extraction_directory = temp_dir / '_archive' + extraction_directory = temp_dir / "_archive" extract_zip(expected_artifact, extraction_directory) - metadata_directory = f'{builder.project_id}.dist-info' - shared_data_directory = f'{builder.project_id}.data' + metadata_directory = f"{builder.project_id}.dist-info" + shared_data_directory = f"{builder.project_id}.data" expected_files = helpers.get_template_files( - 'wheel.standard_default_shared_scripts', + "wheel.standard_default_shared_scripts", project_name, metadata_directory=metadata_directory, shared_data_directory=shared_data_directory, @@ -2251,41 +2251,41 @@ def initialize(self, version, build_data): helpers.assert_files(extraction_directory, expected_files) if not platform.windows: - extracted_binary = extraction_directory / shared_data_directory / 'scripts' / 'binary' + extracted_binary = extraction_directory / shared_data_directory / "scripts" / "binary" assert extracted_binary.stat().st_mode & 0o777 == expected_mode def test_default_extra_metadata(self, hatch, helpers, temp_dir, config_file): - config_file.model.template.plugins['default']['src-layout'] = False + config_file.model.template.plugins["default"]["src-layout"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' + project_path = temp_dir / "my-app" - extra_metadata_path = temp_dir / 'data' + extra_metadata_path = temp_dir / "data" extra_metadata_path.ensure_dir_exists() - (extra_metadata_path / 'foo.txt').touch() - nested_data_path = extra_metadata_path / 'nested' + (extra_metadata_path / "foo.txt").touch() + nested_data_path = extra_metadata_path / "nested" nested_data_path.ensure_dir_exists() - (nested_data_path / 'bar.txt').touch() + (nested_data_path / "bar.txt").touch() config = { - 'project': {'name': project_name, 'requires-python': '>3', 'dynamic': ['version']}, - 'tool': { - 'hatch': { - 'version': {'path': 'my_app/__about__.py'}, - 'build': {'targets': {'wheel': {'versions': ['standard'], 'extra-metadata': {'../data': '/'}}}}, + "project": {"name": project_name, "requires-python": ">3", "dynamic": ["version"]}, + "tool": { + "hatch": { + "version": {"path": "my_app/__about__.py"}, + "build": {"targets": {"wheel": {"versions": ["standard"], "extra-metadata": {"../data": "/"}}}}, }, }, } builder = WheelBuilder(str(project_path), config=config) - build_path = project_path / 'dist' + build_path = project_path / "dist" build_path.mkdir() with project_path.as_cwd(): @@ -2298,39 +2298,39 @@ def test_default_extra_metadata(self, hatch, helpers, temp_dir, config_file): assert len(build_artifacts) == 1 assert expected_artifact == str(build_artifacts[0]) - extraction_directory = temp_dir / '_archive' + extraction_directory = temp_dir / "_archive" extraction_directory.mkdir() - with zipfile.ZipFile(str(expected_artifact), 'r') as zip_archive: + with zipfile.ZipFile(str(expected_artifact), "r") as zip_archive: zip_archive.extractall(str(extraction_directory)) - metadata_directory = f'{builder.project_id}.dist-info' + metadata_directory = f"{builder.project_id}.dist-info" expected_files = helpers.get_template_files( - 'wheel.standard_default_extra_metadata', + "wheel.standard_default_extra_metadata", project_name, metadata_directory=metadata_directory, ) helpers.assert_files(extraction_directory, expected_files) def test_default_extra_metadata_build_data(self, hatch, helpers, temp_dir, config_file): - config_file.model.template.plugins['default']['src-layout'] = False + config_file.model.template.plugins["default"]["src-layout"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' + project_path = temp_dir / "my-app" - extra_metadata_path = temp_dir / 'data' + extra_metadata_path = temp_dir / "data" extra_metadata_path.ensure_dir_exists() - (extra_metadata_path / 'foo.txt').touch() - nested_data_path = extra_metadata_path / 'nested' + (extra_metadata_path / "foo.txt").touch() + nested_data_path = extra_metadata_path / "nested" nested_data_path.ensure_dir_exists() - (nested_data_path / 'bar.txt').touch() + (nested_data_path / "bar.txt").touch() build_script = project_path / DEFAULT_BUILD_SCRIPT build_script.write_text( @@ -2348,17 +2348,17 @@ def initialize(self, version, build_data): ) config = { - 'project': {'name': project_name, 'requires-python': '>3', 'dynamic': ['version']}, - 'tool': { - 'hatch': { - 'version': {'path': 'my_app/__about__.py'}, - 'build': {'targets': {'wheel': {'versions': ['standard'], 'hooks': {'custom': {}}}}}, + "project": {"name": project_name, "requires-python": ">3", "dynamic": ["version"]}, + "tool": { + "hatch": { + "version": {"path": "my_app/__about__.py"}, + "build": {"targets": {"wheel": {"versions": ["standard"], "hooks": {"custom": {}}}}}, }, }, } builder = WheelBuilder(str(project_path), config=config) - build_path = project_path / 'dist' + build_path = project_path / "dist" build_path.mkdir() with project_path.as_cwd(): @@ -2371,15 +2371,15 @@ def initialize(self, version, build_data): assert len(build_artifacts) == 1 assert expected_artifact == str(build_artifacts[0]) - extraction_directory = temp_dir / '_archive' + extraction_directory = temp_dir / "_archive" extraction_directory.mkdir() - with zipfile.ZipFile(str(expected_artifact), 'r') as zip_archive: + with zipfile.ZipFile(str(expected_artifact), "r") as zip_archive: zip_archive.extractall(str(extraction_directory)) - metadata_directory = f'{builder.project_id}.dist-info' + metadata_directory = f"{builder.project_id}.dist-info" expected_files = helpers.get_template_files( - 'wheel.standard_default_extra_metadata', + "wheel.standard_default_extra_metadata", project_name, metadata_directory=metadata_directory, ) @@ -2387,22 +2387,22 @@ def initialize(self, version, build_data): @pytest.mark.requires_unix def test_default_symlink(self, hatch, helpers, temp_dir, config_file): - config_file.model.template.plugins['default']['src-layout'] = False + config_file.model.template.plugins["default"]["src-layout"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' + project_path = temp_dir / "my-app" - vcs_ignore_file = project_path / '.gitignore' - vcs_ignore_file.write_text('*.pyc\n*.so\n*.h') + vcs_ignore_file = project_path / ".gitignore" + vcs_ignore_file.write_text("*.pyc\n*.so\n*.h") - (temp_dir / 'foo.so').write_bytes(b'data') + (temp_dir / "foo.so").write_bytes(b"data") build_script = project_path / DEFAULT_BUILD_SCRIPT build_script.write_text( @@ -2425,21 +2425,21 @@ def initialize(self, version, build_data): ) config = { - 'project': {'name': project_name, 'requires-python': '>3', 'dynamic': ['version']}, - 'tool': { - 'hatch': { - 'version': {'path': 'my_app/__about__.py'}, - 'build': { - 'targets': {'wheel': {'versions': ['standard']}}, - 'artifacts': ['my_app/lib.so'], - 'hooks': {'custom': {'path': DEFAULT_BUILD_SCRIPT}}, + "project": {"name": project_name, "requires-python": ">3", "dynamic": ["version"]}, + "tool": { + "hatch": { + "version": {"path": "my_app/__about__.py"}, + "build": { + "targets": {"wheel": {"versions": ["standard"]}}, + "artifacts": ["my_app/lib.so"], + "hooks": {"custom": {"path": DEFAULT_BUILD_SCRIPT}}, }, }, }, } builder = WheelBuilder(str(project_path), config=config) - build_path = project_path / 'dist' + build_path = project_path / "dist" build_path.mkdir() with project_path.as_cwd(): @@ -2453,18 +2453,18 @@ def initialize(self, version, build_data): assert expected_artifact == str(build_artifacts[0]) best_matching_tag = next(sys_tags()) - tag = f'{best_matching_tag.interpreter}-{best_matching_tag.abi}-{best_matching_tag.platform}' - assert expected_artifact == str(build_path / f'{builder.project_id}-{tag}.whl') + tag = f"{best_matching_tag.interpreter}-{best_matching_tag.abi}-{best_matching_tag.platform}" + assert expected_artifact == str(build_path / f"{builder.project_id}-{tag}.whl") - extraction_directory = temp_dir / '_archive' + extraction_directory = temp_dir / "_archive" extraction_directory.mkdir() - with zipfile.ZipFile(str(expected_artifact), 'r') as zip_archive: + with zipfile.ZipFile(str(expected_artifact), "r") as zip_archive: zip_archive.extractall(str(extraction_directory)) - metadata_directory = f'{builder.project_id}.dist-info' + metadata_directory = f"{builder.project_id}.dist-info" expected_files = helpers.get_template_files( - 'wheel.standard_default_symlink', + "wheel.standard_default_symlink", project_name, metadata_directory=metadata_directory, tag=tag, @@ -2473,27 +2473,27 @@ def initialize(self, version, build_data): @fixed_pathlib_resolution def test_editable_default(self, hatch, helpers, temp_dir): - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' + project_path = temp_dir / "my-app" config = { - 'project': {'name': project_name, 'dynamic': ['version']}, - 'tool': { - 'hatch': { - 'version': {'path': 'src/my_app/__about__.py'}, - 'build': {'targets': {'wheel': {'versions': ['editable']}}}, + "project": {"name": project_name, "dynamic": ["version"]}, + "tool": { + "hatch": { + "version": {"path": "src/my_app/__about__.py"}, + "build": {"targets": {"wheel": {"versions": ["editable"]}}}, }, }, } builder = WheelBuilder(str(project_path), config=config) - build_path = project_path / 'dist' + build_path = project_path / "dist" build_path.mkdir() with project_path.as_cwd(): @@ -2505,39 +2505,39 @@ def test_editable_default(self, hatch, helpers, temp_dir): build_artifacts = list(build_path.iterdir()) assert len(build_artifacts) == 1 assert expected_artifact == str(build_artifacts[0]) - assert expected_artifact == str(build_path / f'{builder.project_id}-{get_python_versions_tag()}-none-any.whl') + assert expected_artifact == str(build_path / f"{builder.project_id}-{get_python_versions_tag()}-none-any.whl") - extraction_directory = temp_dir / '_archive' + extraction_directory = temp_dir / "_archive" extraction_directory.mkdir() - with zipfile.ZipFile(str(expected_artifact), 'r') as zip_archive: + with zipfile.ZipFile(str(expected_artifact), "r") as zip_archive: zip_archive.extractall(str(extraction_directory)) - metadata_directory = f'{builder.project_id}.dist-info' + metadata_directory = f"{builder.project_id}.dist-info" expected_files = helpers.get_template_files( - 'wheel.standard_editable_pth', + "wheel.standard_editable_pth", project_name, metadata_directory=metadata_directory, - package_paths=[str(project_path / 'src')], + package_paths=[str(project_path / "src")], ) helpers.assert_files(extraction_directory, expected_files) # Inspect the archive rather than the extracted files because on Windows they lose their metadata # https://stackoverflow.com/q/9813243 - with zipfile.ZipFile(str(expected_artifact), 'r') as zip_archive: - zip_info = zip_archive.getinfo(f'{metadata_directory}/WHEEL') + with zipfile.ZipFile(str(expected_artifact), "r") as zip_archive: + zip_info = zip_archive.getinfo(f"{metadata_directory}/WHEEL") assert zip_info.date_time == (2020, 2, 2, 0, 0, 0) @fixed_pathlib_resolution def test_editable_default_extra_dependencies(self, hatch, helpers, temp_dir): - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' + project_path = temp_dir / "my-app" build_script = project_path / DEFAULT_BUILD_SCRIPT build_script.write_text( @@ -2555,17 +2555,17 @@ def initialize(self, version, build_data): ) config = { - 'project': {'name': project_name, 'dynamic': ['version']}, - 'tool': { - 'hatch': { - 'version': {'path': 'src/my_app/__about__.py'}, - 'build': {'targets': {'wheel': {'versions': ['editable'], 'hooks': {'custom': {}}}}}, + "project": {"name": project_name, "dynamic": ["version"]}, + "tool": { + "hatch": { + "version": {"path": "src/my_app/__about__.py"}, + "build": {"targets": {"wheel": {"versions": ["editable"], "hooks": {"custom": {}}}}}, }, }, } builder = WheelBuilder(str(project_path), config=config) - build_path = project_path / 'dist' + build_path = project_path / "dist" build_path.mkdir() with project_path.as_cwd(): @@ -2577,39 +2577,39 @@ def initialize(self, version, build_data): build_artifacts = list(build_path.iterdir()) assert len(build_artifacts) == 1 assert expected_artifact == str(build_artifacts[0]) - assert expected_artifact == str(build_path / f'{builder.project_id}-{get_python_versions_tag()}-none-any.whl') + assert expected_artifact == str(build_path / f"{builder.project_id}-{get_python_versions_tag()}-none-any.whl") - extraction_directory = temp_dir / '_archive' + extraction_directory = temp_dir / "_archive" extraction_directory.mkdir() - with zipfile.ZipFile(str(expected_artifact), 'r') as zip_archive: + with zipfile.ZipFile(str(expected_artifact), "r") as zip_archive: zip_archive.extractall(str(extraction_directory)) - metadata_directory = f'{builder.project_id}.dist-info' + metadata_directory = f"{builder.project_id}.dist-info" expected_files = helpers.get_template_files( - 'wheel.standard_editable_pth_extra_dependencies', + "wheel.standard_editable_pth_extra_dependencies", project_name, metadata_directory=metadata_directory, - package_paths=[str(project_path / 'src')], + package_paths=[str(project_path / "src")], ) helpers.assert_files(extraction_directory, expected_files) # Inspect the archive rather than the extracted files because on Windows they lose their metadata # https://stackoverflow.com/q/9813243 - with zipfile.ZipFile(str(expected_artifact), 'r') as zip_archive: - zip_info = zip_archive.getinfo(f'{metadata_directory}/WHEEL') + with zipfile.ZipFile(str(expected_artifact), "r") as zip_archive: + zip_info = zip_archive.getinfo(f"{metadata_directory}/WHEEL") assert zip_info.date_time == (2020, 2, 2, 0, 0, 0) @fixed_pathlib_resolution def test_editable_default_force_include(self, hatch, helpers, temp_dir): - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' + project_path = temp_dir / "my-app" build_script = project_path / DEFAULT_BUILD_SCRIPT build_script.write_text( @@ -2628,17 +2628,17 @@ def initialize(self, version, build_data): ) config = { - 'project': {'name': project_name, 'dynamic': ['version']}, - 'tool': { - 'hatch': { - 'version': {'path': 'src/my_app/__about__.py'}, - 'build': {'targets': {'wheel': {'versions': ['editable'], 'hooks': {'custom': {}}}}}, + "project": {"name": project_name, "dynamic": ["version"]}, + "tool": { + "hatch": { + "version": {"path": "src/my_app/__about__.py"}, + "build": {"targets": {"wheel": {"versions": ["editable"], "hooks": {"custom": {}}}}}, }, }, } builder = WheelBuilder(str(project_path), config=config) - build_path = project_path / 'dist' + build_path = project_path / "dist" build_path.mkdir() with project_path.as_cwd(): @@ -2650,50 +2650,50 @@ def initialize(self, version, build_data): build_artifacts = list(build_path.iterdir()) assert len(build_artifacts) == 1 assert expected_artifact == str(build_artifacts[0]) - assert expected_artifact == str(build_path / f'{builder.project_id}-{get_python_versions_tag()}-none-any.whl') + assert expected_artifact == str(build_path / f"{builder.project_id}-{get_python_versions_tag()}-none-any.whl") - extraction_directory = temp_dir / '_archive' + extraction_directory = temp_dir / "_archive" extraction_directory.mkdir() - with zipfile.ZipFile(str(expected_artifact), 'r') as zip_archive: + with zipfile.ZipFile(str(expected_artifact), "r") as zip_archive: zip_archive.extractall(str(extraction_directory)) - metadata_directory = f'{builder.project_id}.dist-info' + metadata_directory = f"{builder.project_id}.dist-info" expected_files = helpers.get_template_files( - 'wheel.standard_editable_pth_force_include', + "wheel.standard_editable_pth_force_include", project_name, metadata_directory=metadata_directory, - package_paths=[str(project_path / 'src')], + package_paths=[str(project_path / "src")], ) helpers.assert_files(extraction_directory, expected_files) # Inspect the archive rather than the extracted files because on Windows they lose their metadata # https://stackoverflow.com/q/9813243 - with zipfile.ZipFile(str(expected_artifact), 'r') as zip_archive: - zip_info = zip_archive.getinfo(f'{metadata_directory}/WHEEL') + with zipfile.ZipFile(str(expected_artifact), "r") as zip_archive: + zip_info = zip_archive.getinfo(f"{metadata_directory}/WHEEL") assert zip_info.date_time == (2020, 2, 2, 0, 0, 0) @fixed_pathlib_resolution def test_editable_default_force_include_option(self, hatch, helpers, temp_dir): - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' + project_path = temp_dir / "my-app" config = { - 'project': {'name': project_name, 'dynamic': ['version']}, - 'tool': { - 'hatch': { - 'version': {'path': 'src/my_app/__about__.py'}, - 'build': { - 'targets': { - 'wheel': { - 'versions': ['editable'], - 'force-include': {'src/my_app/__about__.py': 'zfoo.py'}, + "project": {"name": project_name, "dynamic": ["version"]}, + "tool": { + "hatch": { + "version": {"path": "src/my_app/__about__.py"}, + "build": { + "targets": { + "wheel": { + "versions": ["editable"], + "force-include": {"src/my_app/__about__.py": "zfoo.py"}, } } }, @@ -2702,7 +2702,7 @@ def test_editable_default_force_include_option(self, hatch, helpers, temp_dir): } builder = WheelBuilder(str(project_path), config=config) - build_path = project_path / 'dist' + build_path = project_path / "dist" build_path.mkdir() with project_path.as_cwd(): @@ -2714,55 +2714,55 @@ def test_editable_default_force_include_option(self, hatch, helpers, temp_dir): build_artifacts = list(build_path.iterdir()) assert len(build_artifacts) == 1 assert expected_artifact == str(build_artifacts[0]) - assert expected_artifact == str(build_path / f'{builder.project_id}-{get_python_versions_tag()}-none-any.whl') + assert expected_artifact == str(build_path / f"{builder.project_id}-{get_python_versions_tag()}-none-any.whl") - extraction_directory = temp_dir / '_archive' + extraction_directory = temp_dir / "_archive" extraction_directory.mkdir() - with zipfile.ZipFile(str(expected_artifact), 'r') as zip_archive: + with zipfile.ZipFile(str(expected_artifact), "r") as zip_archive: zip_archive.extractall(str(extraction_directory)) - metadata_directory = f'{builder.project_id}.dist-info' + metadata_directory = f"{builder.project_id}.dist-info" expected_files = helpers.get_template_files( - 'wheel.standard_editable_pth_force_include', + "wheel.standard_editable_pth_force_include", project_name, metadata_directory=metadata_directory, - package_paths=[str(project_path / 'src')], + package_paths=[str(project_path / "src")], ) helpers.assert_files(extraction_directory, expected_files) # Inspect the archive rather than the extracted files because on Windows they lose their metadata # https://stackoverflow.com/q/9813243 - with zipfile.ZipFile(str(expected_artifact), 'r') as zip_archive: - zip_info = zip_archive.getinfo(f'{metadata_directory}/WHEEL') + with zipfile.ZipFile(str(expected_artifact), "r") as zip_archive: + zip_info = zip_archive.getinfo(f"{metadata_directory}/WHEEL") assert zip_info.date_time == (2020, 2, 2, 0, 0, 0) @pytest.mark.requires_unix def test_editable_default_symlink(self, hatch, helpers, temp_dir): - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - symlink = project_path / '_' / 'my_app' + project_path = temp_dir / "my-app" + symlink = project_path / "_" / "my_app" symlink.parent.ensure_dir_exists() - symlink.symlink_to(project_path / 'src' / 'my_app') + symlink.symlink_to(project_path / "src" / "my_app") config = { - 'project': {'name': project_name, 'dynamic': ['version']}, - 'tool': { - 'hatch': { - 'version': {'path': 'src/my_app/__about__.py'}, - 'build': {'targets': {'wheel': {'versions': ['editable']}}}, + "project": {"name": project_name, "dynamic": ["version"]}, + "tool": { + "hatch": { + "version": {"path": "src/my_app/__about__.py"}, + "build": {"targets": {"wheel": {"versions": ["editable"]}}}, }, }, } builder = WheelBuilder(str(project_path), config=config) - build_path = project_path / 'dist' + build_path = project_path / "dist" build_path.mkdir() with project_path.as_cwd(): @@ -2774,55 +2774,55 @@ def test_editable_default_symlink(self, hatch, helpers, temp_dir): build_artifacts = list(build_path.iterdir()) assert len(build_artifacts) == 1 assert expected_artifact == str(build_artifacts[0]) - assert expected_artifact == str(build_path / f'{builder.project_id}-{get_python_versions_tag()}-none-any.whl') + assert expected_artifact == str(build_path / f"{builder.project_id}-{get_python_versions_tag()}-none-any.whl") - extraction_directory = temp_dir / '_archive' + extraction_directory = temp_dir / "_archive" extraction_directory.mkdir() - with zipfile.ZipFile(str(expected_artifact), 'r') as zip_archive: + with zipfile.ZipFile(str(expected_artifact), "r") as zip_archive: zip_archive.extractall(str(extraction_directory)) - metadata_directory = f'{builder.project_id}.dist-info' + metadata_directory = f"{builder.project_id}.dist-info" expected_files = helpers.get_template_files( - 'wheel.standard_editable_pth', + "wheel.standard_editable_pth", project_name, metadata_directory=metadata_directory, - package_paths=[str(project_path / 'src')], + package_paths=[str(project_path / "src")], ) helpers.assert_files(extraction_directory, expected_files) # Inspect the archive rather than the extracted files because on Windows they lose their metadata # https://stackoverflow.com/q/9813243 - with zipfile.ZipFile(str(expected_artifact), 'r') as zip_archive: - zip_info = zip_archive.getinfo(f'{metadata_directory}/WHEEL') + with zipfile.ZipFile(str(expected_artifact), "r") as zip_archive: + zip_info = zip_archive.getinfo(f"{metadata_directory}/WHEEL") assert zip_info.date_time == (2020, 2, 2, 0, 0, 0) @fixed_pathlib_resolution def test_editable_exact(self, hatch, helpers, temp_dir, config_file): - config_file.model.template.plugins['default']['src-layout'] = False + config_file.model.template.plugins["default"]["src-layout"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' + project_path = temp_dir / "my-app" config = { - 'project': {'name': project_name, 'dynamic': ['version']}, - 'tool': { - 'hatch': { - 'version': {'path': 'my_app/__about__.py'}, - 'build': {'targets': {'wheel': {'versions': ['editable'], 'dev-mode-exact': True}}}, + "project": {"name": project_name, "dynamic": ["version"]}, + "tool": { + "hatch": { + "version": {"path": "my_app/__about__.py"}, + "build": {"targets": {"wheel": {"versions": ["editable"], "dev-mode-exact": True}}}, }, }, } builder = WheelBuilder(str(project_path), config=config) - build_path = project_path / 'dist' + build_path = project_path / "dist" build_path.mkdir() with project_path.as_cwd(): @@ -2834,42 +2834,42 @@ def test_editable_exact(self, hatch, helpers, temp_dir, config_file): build_artifacts = list(build_path.iterdir()) assert len(build_artifacts) == 1 assert expected_artifact == str(build_artifacts[0]) - assert expected_artifact == str(build_path / f'{builder.project_id}-{get_python_versions_tag()}-none-any.whl') + assert expected_artifact == str(build_path / f"{builder.project_id}-{get_python_versions_tag()}-none-any.whl") - extraction_directory = temp_dir / '_archive' + extraction_directory = temp_dir / "_archive" extraction_directory.mkdir() - with zipfile.ZipFile(str(expected_artifact), 'r') as zip_archive: + with zipfile.ZipFile(str(expected_artifact), "r") as zip_archive: zip_archive.extractall(str(extraction_directory)) - metadata_directory = f'{builder.project_id}.dist-info' + metadata_directory = f"{builder.project_id}.dist-info" expected_files = helpers.get_template_files( - 'wheel.standard_editable_exact', + "wheel.standard_editable_exact", project_name, metadata_directory=metadata_directory, - package_root=str(project_path / 'my_app' / '__init__.py'), + package_root=str(project_path / "my_app" / "__init__.py"), ) helpers.assert_files(extraction_directory, expected_files) # Inspect the archive rather than the extracted files because on Windows they lose their metadata # https://stackoverflow.com/q/9813243 - with zipfile.ZipFile(str(expected_artifact), 'r') as zip_archive: - zip_info = zip_archive.getinfo(f'{metadata_directory}/WHEEL') + with zipfile.ZipFile(str(expected_artifact), "r") as zip_archive: + zip_info = zip_archive.getinfo(f"{metadata_directory}/WHEEL") assert zip_info.date_time == (2020, 2, 2, 0, 0, 0) @fixed_pathlib_resolution def test_editable_exact_extra_dependencies(self, hatch, helpers, temp_dir, config_file): - config_file.model.template.plugins['default']['src-layout'] = False + config_file.model.template.plugins["default"]["src-layout"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' + project_path = temp_dir / "my-app" build_script = project_path / DEFAULT_BUILD_SCRIPT build_script.write_text( @@ -2887,13 +2887,13 @@ def initialize(self, version, build_data): ) config = { - 'project': {'name': project_name, 'dynamic': ['version']}, - 'tool': { - 'hatch': { - 'version': {'path': 'my_app/__about__.py'}, - 'build': { - 'targets': { - 'wheel': {'versions': ['editable'], 'dev-mode-exact': True, 'hooks': {'custom': {}}} + "project": {"name": project_name, "dynamic": ["version"]}, + "tool": { + "hatch": { + "version": {"path": "my_app/__about__.py"}, + "build": { + "targets": { + "wheel": {"versions": ["editable"], "dev-mode-exact": True, "hooks": {"custom": {}}} } }, }, @@ -2901,7 +2901,7 @@ def initialize(self, version, build_data): } builder = WheelBuilder(str(project_path), config=config) - build_path = project_path / 'dist' + build_path = project_path / "dist" build_path.mkdir() with project_path.as_cwd(): @@ -2913,42 +2913,42 @@ def initialize(self, version, build_data): build_artifacts = list(build_path.iterdir()) assert len(build_artifacts) == 1 assert expected_artifact == str(build_artifacts[0]) - assert expected_artifact == str(build_path / f'{builder.project_id}-{get_python_versions_tag()}-none-any.whl') + assert expected_artifact == str(build_path / f"{builder.project_id}-{get_python_versions_tag()}-none-any.whl") - extraction_directory = temp_dir / '_archive' + extraction_directory = temp_dir / "_archive" extraction_directory.mkdir() - with zipfile.ZipFile(str(expected_artifact), 'r') as zip_archive: + with zipfile.ZipFile(str(expected_artifact), "r") as zip_archive: zip_archive.extractall(str(extraction_directory)) - metadata_directory = f'{builder.project_id}.dist-info' + metadata_directory = f"{builder.project_id}.dist-info" expected_files = helpers.get_template_files( - 'wheel.standard_editable_exact_extra_dependencies', + "wheel.standard_editable_exact_extra_dependencies", project_name, metadata_directory=metadata_directory, - package_root=str(project_path / 'my_app' / '__init__.py'), + package_root=str(project_path / "my_app" / "__init__.py"), ) helpers.assert_files(extraction_directory, expected_files) # Inspect the archive rather than the extracted files because on Windows they lose their metadata # https://stackoverflow.com/q/9813243 - with zipfile.ZipFile(str(expected_artifact), 'r') as zip_archive: - zip_info = zip_archive.getinfo(f'{metadata_directory}/WHEEL') + with zipfile.ZipFile(str(expected_artifact), "r") as zip_archive: + zip_info = zip_archive.getinfo(f"{metadata_directory}/WHEEL") assert zip_info.date_time == (2020, 2, 2, 0, 0, 0) @fixed_pathlib_resolution def test_editable_exact_force_include(self, hatch, helpers, temp_dir, config_file): - config_file.model.template.plugins['default']['src-layout'] = False + config_file.model.template.plugins["default"]["src-layout"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' + project_path = temp_dir / "my-app" build_script = project_path / DEFAULT_BUILD_SCRIPT build_script.write_text( @@ -2967,13 +2967,13 @@ def initialize(self, version, build_data): ) config = { - 'project': {'name': project_name, 'dynamic': ['version']}, - 'tool': { - 'hatch': { - 'version': {'path': 'my_app/__about__.py'}, - 'build': { - 'targets': { - 'wheel': {'versions': ['editable'], 'dev-mode-exact': True, 'hooks': {'custom': {}}} + "project": {"name": project_name, "dynamic": ["version"]}, + "tool": { + "hatch": { + "version": {"path": "my_app/__about__.py"}, + "build": { + "targets": { + "wheel": {"versions": ["editable"], "dev-mode-exact": True, "hooks": {"custom": {}}} } }, }, @@ -2981,7 +2981,7 @@ def initialize(self, version, build_data): } builder = WheelBuilder(str(project_path), config=config) - build_path = project_path / 'dist' + build_path = project_path / "dist" build_path.mkdir() with project_path.as_cwd(): @@ -2993,54 +2993,54 @@ def initialize(self, version, build_data): build_artifacts = list(build_path.iterdir()) assert len(build_artifacts) == 1 assert expected_artifact == str(build_artifacts[0]) - assert expected_artifact == str(build_path / f'{builder.project_id}-{get_python_versions_tag()}-none-any.whl') + assert expected_artifact == str(build_path / f"{builder.project_id}-{get_python_versions_tag()}-none-any.whl") - extraction_directory = temp_dir / '_archive' + extraction_directory = temp_dir / "_archive" extraction_directory.mkdir() - with zipfile.ZipFile(str(expected_artifact), 'r') as zip_archive: + with zipfile.ZipFile(str(expected_artifact), "r") as zip_archive: zip_archive.extractall(str(extraction_directory)) - metadata_directory = f'{builder.project_id}.dist-info' + metadata_directory = f"{builder.project_id}.dist-info" expected_files = helpers.get_template_files( - 'wheel.standard_editable_exact_force_include', + "wheel.standard_editable_exact_force_include", project_name, metadata_directory=metadata_directory, - package_root=str(project_path / 'my_app' / '__init__.py'), + package_root=str(project_path / "my_app" / "__init__.py"), ) helpers.assert_files(extraction_directory, expected_files) # Inspect the archive rather than the extracted files because on Windows they lose their metadata # https://stackoverflow.com/q/9813243 - with zipfile.ZipFile(str(expected_artifact), 'r') as zip_archive: - zip_info = zip_archive.getinfo(f'{metadata_directory}/WHEEL') + with zipfile.ZipFile(str(expected_artifact), "r") as zip_archive: + zip_info = zip_archive.getinfo(f"{metadata_directory}/WHEEL") assert zip_info.date_time == (2020, 2, 2, 0, 0, 0) @fixed_pathlib_resolution def test_editable_exact_force_include_option(self, hatch, helpers, temp_dir, config_file): - config_file.model.template.plugins['default']['src-layout'] = False + config_file.model.template.plugins["default"]["src-layout"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' + project_path = temp_dir / "my-app" config = { - 'project': {'name': project_name, 'dynamic': ['version']}, - 'tool': { - 'hatch': { - 'version': {'path': 'my_app/__about__.py'}, - 'build': { - 'targets': { - 'wheel': { - 'versions': ['editable'], - 'dev-mode-exact': True, - 'force-include': {'my_app/__about__.py': 'zfoo.py'}, + "project": {"name": project_name, "dynamic": ["version"]}, + "tool": { + "hatch": { + "version": {"path": "my_app/__about__.py"}, + "build": { + "targets": { + "wheel": { + "versions": ["editable"], + "dev-mode-exact": True, + "force-include": {"my_app/__about__.py": "zfoo.py"}, } } }, @@ -3049,7 +3049,7 @@ def test_editable_exact_force_include_option(self, hatch, helpers, temp_dir, con } builder = WheelBuilder(str(project_path), config=config) - build_path = project_path / 'dist' + build_path = project_path / "dist" build_path.mkdir() with project_path.as_cwd(): @@ -3061,42 +3061,42 @@ def test_editable_exact_force_include_option(self, hatch, helpers, temp_dir, con build_artifacts = list(build_path.iterdir()) assert len(build_artifacts) == 1 assert expected_artifact == str(build_artifacts[0]) - assert expected_artifact == str(build_path / f'{builder.project_id}-{get_python_versions_tag()}-none-any.whl') + assert expected_artifact == str(build_path / f"{builder.project_id}-{get_python_versions_tag()}-none-any.whl") - extraction_directory = temp_dir / '_archive' + extraction_directory = temp_dir / "_archive" extraction_directory.mkdir() - with zipfile.ZipFile(str(expected_artifact), 'r') as zip_archive: + with zipfile.ZipFile(str(expected_artifact), "r") as zip_archive: zip_archive.extractall(str(extraction_directory)) - metadata_directory = f'{builder.project_id}.dist-info' + metadata_directory = f"{builder.project_id}.dist-info" expected_files = helpers.get_template_files( - 'wheel.standard_editable_exact_force_include', + "wheel.standard_editable_exact_force_include", project_name, metadata_directory=metadata_directory, - package_root=str(project_path / 'my_app' / '__init__.py'), + package_root=str(project_path / "my_app" / "__init__.py"), ) helpers.assert_files(extraction_directory, expected_files) # Inspect the archive rather than the extracted files because on Windows they lose their metadata # https://stackoverflow.com/q/9813243 - with zipfile.ZipFile(str(expected_artifact), 'r') as zip_archive: - zip_info = zip_archive.getinfo(f'{metadata_directory}/WHEEL') + with zipfile.ZipFile(str(expected_artifact), "r") as zip_archive: + zip_info = zip_archive.getinfo(f"{metadata_directory}/WHEEL") assert zip_info.date_time == (2020, 2, 2, 0, 0, 0) @fixed_pathlib_resolution def test_editable_exact_force_include_build_data_precedence(self, hatch, helpers, temp_dir, config_file): - config_file.model.template.plugins['default']['src-layout'] = False + config_file.model.template.plugins["default"]["src-layout"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' + project_path = temp_dir / "my-app" build_script = project_path / DEFAULT_BUILD_SCRIPT build_script.write_text( @@ -3115,17 +3115,17 @@ def initialize(self, version, build_data): ) config = { - 'project': {'name': project_name, 'dynamic': ['version']}, - 'tool': { - 'hatch': { - 'version': {'path': 'my_app/__about__.py'}, - 'build': { - 'targets': { - 'wheel': { - 'versions': ['editable'], - 'dev-mode-exact': True, - 'force-include': {'my_app/__about__.py': 'zbar.py'}, - 'hooks': {'custom': {}}, + "project": {"name": project_name, "dynamic": ["version"]}, + "tool": { + "hatch": { + "version": {"path": "my_app/__about__.py"}, + "build": { + "targets": { + "wheel": { + "versions": ["editable"], + "dev-mode-exact": True, + "force-include": {"my_app/__about__.py": "zbar.py"}, + "hooks": {"custom": {}}, } } }, @@ -3134,7 +3134,7 @@ def initialize(self, version, build_data): } builder = WheelBuilder(str(project_path), config=config) - build_path = project_path / 'dist' + build_path = project_path / "dist" build_path.mkdir() with project_path.as_cwd(): @@ -3146,52 +3146,52 @@ def initialize(self, version, build_data): build_artifacts = list(build_path.iterdir()) assert len(build_artifacts) == 1 assert expected_artifact == str(build_artifacts[0]) - assert expected_artifact == str(build_path / f'{builder.project_id}-{get_python_versions_tag()}-none-any.whl') + assert expected_artifact == str(build_path / f"{builder.project_id}-{get_python_versions_tag()}-none-any.whl") - extraction_directory = temp_dir / '_archive' + extraction_directory = temp_dir / "_archive" extraction_directory.mkdir() - with zipfile.ZipFile(str(expected_artifact), 'r') as zip_archive: + with zipfile.ZipFile(str(expected_artifact), "r") as zip_archive: zip_archive.extractall(str(extraction_directory)) - metadata_directory = f'{builder.project_id}.dist-info' + metadata_directory = f"{builder.project_id}.dist-info" expected_files = helpers.get_template_files( - 'wheel.standard_editable_exact_force_include', + "wheel.standard_editable_exact_force_include", project_name, metadata_directory=metadata_directory, - package_root=str(project_path / 'my_app' / '__init__.py'), + package_root=str(project_path / "my_app" / "__init__.py"), ) helpers.assert_files(extraction_directory, expected_files) # Inspect the archive rather than the extracted files because on Windows they lose their metadata # https://stackoverflow.com/q/9813243 - with zipfile.ZipFile(str(expected_artifact), 'r') as zip_archive: - zip_info = zip_archive.getinfo(f'{metadata_directory}/WHEEL') + with zipfile.ZipFile(str(expected_artifact), "r") as zip_archive: + zip_info = zip_archive.getinfo(f"{metadata_directory}/WHEEL") assert zip_info.date_time == (2020, 2, 2, 0, 0, 0) @fixed_pathlib_resolution def test_editable_pth(self, hatch, helpers, temp_dir): - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' + project_path = temp_dir / "my-app" config = { - 'project': {'name': project_name, 'dynamic': ['version']}, - 'tool': { - 'hatch': { - 'version': {'path': 'src/my_app/__about__.py'}, - 'build': {'targets': {'wheel': {'versions': ['editable'], 'dev-mode-dirs': ['.']}}}, + "project": {"name": project_name, "dynamic": ["version"]}, + "tool": { + "hatch": { + "version": {"path": "src/my_app/__about__.py"}, + "build": {"targets": {"wheel": {"versions": ["editable"], "dev-mode-dirs": ["."]}}}, }, }, } builder = WheelBuilder(str(project_path), config=config) - build_path = project_path / 'dist' + build_path = project_path / "dist" build_path.mkdir() with project_path.as_cwd(): @@ -3203,17 +3203,17 @@ def test_editable_pth(self, hatch, helpers, temp_dir): build_artifacts = list(build_path.iterdir()) assert len(build_artifacts) == 1 assert expected_artifact == str(build_artifacts[0]) - assert expected_artifact == str(build_path / f'{builder.project_id}-{get_python_versions_tag()}-none-any.whl') + assert expected_artifact == str(build_path / f"{builder.project_id}-{get_python_versions_tag()}-none-any.whl") - extraction_directory = temp_dir / '_archive' + extraction_directory = temp_dir / "_archive" extraction_directory.mkdir() - with zipfile.ZipFile(str(expected_artifact), 'r') as zip_archive: + with zipfile.ZipFile(str(expected_artifact), "r") as zip_archive: zip_archive.extractall(str(extraction_directory)) - metadata_directory = f'{builder.project_id}.dist-info' + metadata_directory = f"{builder.project_id}.dist-info" expected_files = helpers.get_template_files( - 'wheel.standard_editable_pth', + "wheel.standard_editable_pth", project_name, metadata_directory=metadata_directory, package_paths=[str(project_path)], @@ -3222,39 +3222,39 @@ def test_editable_pth(self, hatch, helpers, temp_dir): # Inspect the archive rather than the extracted files because on Windows they lose their metadata # https://stackoverflow.com/q/9813243 - with zipfile.ZipFile(str(expected_artifact), 'r') as zip_archive: - zip_info = zip_archive.getinfo(f'{metadata_directory}/WHEEL') + with zipfile.ZipFile(str(expected_artifact), "r") as zip_archive: + zip_info = zip_archive.getinfo(f"{metadata_directory}/WHEEL") assert zip_info.date_time == (2020, 2, 2, 0, 0, 0) def test_default_namespace_package(self, hatch, helpers, temp_dir, config_file): - config_file.model.template.plugins['default']['src-layout'] = False + config_file.model.template.plugins["default"]["src-layout"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - package_path = project_path / 'my_app' - namespace_path = project_path / 'namespace' + project_path = temp_dir / "my-app" + package_path = project_path / "my_app" + namespace_path = project_path / "namespace" namespace_path.mkdir() - package_path.replace(namespace_path / 'my_app') + package_path.replace(namespace_path / "my_app") config = { - 'project': {'name': project_name, 'dynamic': ['version']}, - 'tool': { - 'hatch': { - 'version': {'path': 'namespace/my_app/__about__.py'}, - 'build': {'targets': {'wheel': {'versions': ['standard']}}}, + "project": {"name": project_name, "dynamic": ["version"]}, + "tool": { + "hatch": { + "version": {"path": "namespace/my_app/__about__.py"}, + "build": {"targets": {"wheel": {"versions": ["standard"]}}}, }, }, } builder = WheelBuilder(str(project_path), config=config) - build_path = project_path / 'dist' + build_path = project_path / "dist" with project_path.as_cwd(): artifacts = list(builder.build()) @@ -3265,48 +3265,48 @@ def test_default_namespace_package(self, hatch, helpers, temp_dir, config_file): build_artifacts = list(build_path.iterdir()) assert len(build_artifacts) == 1 assert expected_artifact == str(build_artifacts[0]) - assert expected_artifact == str(build_path / f'{builder.project_id}-{get_python_versions_tag()}-none-any.whl') + assert expected_artifact == str(build_path / f"{builder.project_id}-{get_python_versions_tag()}-none-any.whl") - extraction_directory = temp_dir / '_archive' + extraction_directory = temp_dir / "_archive" extraction_directory.mkdir() - with zipfile.ZipFile(str(expected_artifact), 'r') as zip_archive: + with zipfile.ZipFile(str(expected_artifact), "r") as zip_archive: zip_archive.extractall(str(extraction_directory)) - metadata_directory = f'{builder.project_id}.dist-info' + metadata_directory = f"{builder.project_id}.dist-info" expected_files = helpers.get_template_files( - 'wheel.standard_default_namespace_package', + "wheel.standard_default_namespace_package", project_name, metadata_directory=metadata_directory, - namespace='namespace', + namespace="namespace", ) helpers.assert_files(extraction_directory, expected_files) def test_default_entry_points(self, hatch, helpers, temp_dir, config_file): - config_file.model.template.plugins['default']['src-layout'] = False + config_file.model.template.plugins["default"]["src-layout"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' + project_path = temp_dir / "my-app" config = { - 'project': {'name': project_name, 'dynamic': ['version'], 'scripts': {'foo': 'pkg:bar', 'bar': 'pkg:foo'}}, - 'tool': { - 'hatch': { - 'version': {'path': 'my_app/__about__.py'}, - 'build': {'targets': {'wheel': {'versions': ['standard']}}}, + "project": {"name": project_name, "dynamic": ["version"], "scripts": {"foo": "pkg:bar", "bar": "pkg:foo"}}, + "tool": { + "hatch": { + "version": {"path": "my_app/__about__.py"}, + "build": {"targets": {"wheel": {"versions": ["standard"]}}}, }, }, } builder = WheelBuilder(str(project_path), config=config) - build_path = project_path / 'dist' + build_path = project_path / "dist" with project_path.as_cwd(): artifacts = list(builder.build()) @@ -3317,42 +3317,42 @@ def test_default_entry_points(self, hatch, helpers, temp_dir, config_file): build_artifacts = list(build_path.iterdir()) assert len(build_artifacts) == 1 assert expected_artifact == str(build_artifacts[0]) - assert expected_artifact == str(build_path / f'{builder.project_id}-{get_python_versions_tag()}-none-any.whl') + assert expected_artifact == str(build_path / f"{builder.project_id}-{get_python_versions_tag()}-none-any.whl") - extraction_directory = temp_dir / '_archive' + extraction_directory = temp_dir / "_archive" extraction_directory.mkdir() - with zipfile.ZipFile(str(expected_artifact), 'r') as zip_archive: + with zipfile.ZipFile(str(expected_artifact), "r") as zip_archive: zip_archive.extractall(str(extraction_directory)) - metadata_directory = f'{builder.project_id}.dist-info' + metadata_directory = f"{builder.project_id}.dist-info" expected_files = helpers.get_template_files( - 'wheel.standard_entry_points', project_name, metadata_directory=metadata_directory + "wheel.standard_entry_points", project_name, metadata_directory=metadata_directory ) helpers.assert_files(extraction_directory, expected_files) def test_explicit_selection_with_src_layout(self, hatch, helpers, temp_dir): - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' + project_path = temp_dir / "my-app" config = { - 'project': {'name': project_name, 'dynamic': ['version']}, - 'tool': { - 'hatch': { - 'version': {'path': 'src/my_app/__about__.py'}, - 'build': { - 'targets': { - 'wheel': { - 'versions': ['standard'], - 'artifacts': ['README.md'], - 'only-include': ['src/my_app'], - 'sources': ['src'], + "project": {"name": project_name, "dynamic": ["version"]}, + "tool": { + "hatch": { + "version": {"path": "src/my_app/__about__.py"}, + "build": { + "targets": { + "wheel": { + "versions": ["standard"], + "artifacts": ["README.md"], + "only-include": ["src/my_app"], + "sources": ["src"], } }, }, @@ -3361,7 +3361,7 @@ def test_explicit_selection_with_src_layout(self, hatch, helpers, temp_dir): } builder = WheelBuilder(str(project_path), config=config) - build_path = project_path / 'dist' + build_path = project_path / "dist" build_path.mkdir() with project_path.as_cwd(): @@ -3374,39 +3374,39 @@ def test_explicit_selection_with_src_layout(self, hatch, helpers, temp_dir): assert len(build_artifacts) == 1 assert expected_artifact == str(build_artifacts[0]) - extraction_directory = temp_dir / '_archive' + extraction_directory = temp_dir / "_archive" extraction_directory.mkdir() - with zipfile.ZipFile(str(expected_artifact), 'r') as zip_archive: + with zipfile.ZipFile(str(expected_artifact), "r") as zip_archive: zip_archive.extractall(str(extraction_directory)) - metadata_directory = f'{builder.project_id}.dist-info' + metadata_directory = f"{builder.project_id}.dist-info" expected_files = helpers.get_template_files( - 'wheel.standard_default_license_single', + "wheel.standard_default_license_single", project_name, metadata_directory=metadata_directory, ) helpers.assert_files(extraction_directory, expected_files) def test_single_module(self, hatch, helpers, temp_dir, config_file): - config_file.model.template.plugins['default']['src-layout'] = False + config_file.model.template.plugins["default"]["src-layout"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - (project_path / 'my_app').remove() - (project_path / 'my_app.py').touch() + project_path = temp_dir / "my-app" + (project_path / "my_app").remove() + (project_path / "my_app.py").touch() - config = {'project': {'name': project_name, 'version': '0.0.1'}} + config = {"project": {"name": project_name, "version": "0.0.1"}} builder = WheelBuilder(str(project_path), config=config) - build_path = project_path / 'dist' + build_path = project_path / "dist" build_path.mkdir() with project_path.as_cwd(): @@ -3419,45 +3419,45 @@ def test_single_module(self, hatch, helpers, temp_dir, config_file): assert len(build_artifacts) == 1 assert expected_artifact == str(build_artifacts[0]) - extraction_directory = temp_dir / '_archive' + extraction_directory = temp_dir / "_archive" extraction_directory.mkdir() - with zipfile.ZipFile(str(expected_artifact), 'r') as zip_archive: + with zipfile.ZipFile(str(expected_artifact), "r") as zip_archive: zip_archive.extractall(str(extraction_directory)) - metadata_directory = f'{builder.project_id}.dist-info' + metadata_directory = f"{builder.project_id}.dist-info" expected_files = helpers.get_template_files( - 'wheel.standard_default_single_module', + "wheel.standard_default_single_module", project_name, metadata_directory=metadata_directory, ) helpers.assert_files(extraction_directory, expected_files) def test_no_strict_naming(self, hatch, helpers, temp_dir, config_file): - config_file.model.template.plugins['default']['src-layout'] = False + config_file.model.template.plugins["default"]["src-layout"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' + project_path = temp_dir / "my-app" config = { - 'project': {'name': project_name, 'dynamic': ['version']}, - 'tool': { - 'hatch': { - 'version': {'path': 'my_app/__about__.py'}, - 'build': {'targets': {'wheel': {'versions': ['standard'], 'strict-naming': False}}}, + "project": {"name": project_name, "dynamic": ["version"]}, + "tool": { + "hatch": { + "version": {"path": "my_app/__about__.py"}, + "build": {"targets": {"wheel": {"versions": ["standard"], "strict-naming": False}}}, }, }, } builder = WheelBuilder(str(project_path), config=config) - build_path = project_path / 'dist' + build_path = project_path / "dist" with project_path.as_cwd(): artifacts = list(builder.build()) @@ -3469,42 +3469,42 @@ def test_no_strict_naming(self, hatch, helpers, temp_dir, config_file): assert len(build_artifacts) == 1 assert expected_artifact == str(build_artifacts[0]) assert expected_artifact == str( - build_path / f'{builder.artifact_project_id}-{get_python_versions_tag()}-none-any.whl' + build_path / f"{builder.artifact_project_id}-{get_python_versions_tag()}-none-any.whl" ) - extraction_directory = temp_dir / '_archive' + extraction_directory = temp_dir / "_archive" extraction_directory.mkdir() - with zipfile.ZipFile(str(expected_artifact), 'r') as zip_archive: + with zipfile.ZipFile(str(expected_artifact), "r") as zip_archive: zip_archive.extractall(str(extraction_directory)) - metadata_directory = f'{builder.artifact_project_id}.dist-info' + metadata_directory = f"{builder.artifact_project_id}.dist-info" expected_files = helpers.get_template_files( - 'wheel.standard_no_strict_naming', project_name, metadata_directory=metadata_directory + "wheel.standard_no_strict_naming", project_name, metadata_directory=metadata_directory ) helpers.assert_files(extraction_directory, expected_files) def test_editable_sources_rewrite_error(self, hatch, temp_dir): - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' + project_path = temp_dir / "my-app" config = { - 'project': {'name': project_name, 'dynamic': ['version']}, - 'tool': { - 'hatch': { - 'version': {'path': 'src/my_app/__about__.py'}, - 'build': { - 'targets': { - 'wheel': { - 'versions': ['editable'], - 'only-include': ['src/my_app'], - 'sources': {'src/my_app': 'namespace/plugins/my_app'}, + "project": {"name": project_name, "dynamic": ["version"]}, + "tool": { + "hatch": { + "version": {"path": "src/my_app/__about__.py"}, + "build": { + "targets": { + "wheel": { + "versions": ["editable"], + "only-include": ["src/my_app"], + "sources": {"src/my_app": "namespace/plugins/my_app"}, } }, }, @@ -3513,42 +3513,45 @@ def test_editable_sources_rewrite_error(self, hatch, temp_dir): } builder = WheelBuilder(str(project_path), config=config) - build_path = project_path / 'dist' + build_path = project_path / "dist" build_path.mkdir() - with project_path.as_cwd(), pytest.raises( - ValueError, - match=( - 'Dev mode installations are unsupported when any path rewrite in the `sources` option ' - 'changes a prefix rather than removes it, see: ' - 'https://github.com/pfmoore/editables/issues/20' + with ( + project_path.as_cwd(), + pytest.raises( + ValueError, + match=( + "Dev mode installations are unsupported when any path rewrite in the `sources` option " + "changes a prefix rather than removes it, see: " + "https://github.com/pfmoore/editables/issues/20" + ), ), ): list(builder.build(directory=str(build_path))) @pytest.mark.skipif( - sys.platform != 'darwin' or sys.version_info < (3, 8), - reason='requires support for ARM on macOS', + sys.platform != "darwin" or sys.version_info < (3, 8), + reason="requires support for ARM on macOS", ) @pytest.mark.parametrize( - ('archflags', 'expected_arch'), - [('-arch x86_64', 'x86_64'), ('-arch arm64', 'arm64'), ('-arch arm64 -arch x86_64', 'universal2')], + ("archflags", "expected_arch"), + [("-arch x86_64", "x86_64"), ("-arch arm64", "arm64"), ("-arch arm64 -arch x86_64", "universal2")], ) def test_macos_archflags(self, hatch, helpers, temp_dir, config_file, archflags, expected_arch): - config_file.model.template.plugins['default']['src-layout'] = False + config_file.model.template.plugins["default"]["src-layout"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' + project_path = temp_dir / "my-app" - vcs_ignore_file = project_path / '.gitignore' - vcs_ignore_file.write_text('*.pyc\n*.so\n*.h') + vcs_ignore_file = project_path / ".gitignore" + vcs_ignore_file.write_text("*.pyc\n*.so\n*.h") build_script = project_path / DEFAULT_BUILD_SCRIPT build_script.write_text( @@ -3570,24 +3573,24 @@ def initialize(self, version, build_data): ) config = { - 'project': {'name': project_name, 'requires-python': '>3', 'dynamic': ['version']}, - 'tool': { - 'hatch': { - 'version': {'path': 'my_app/__about__.py'}, - 'build': { - 'targets': {'wheel': {'versions': ['standard']}}, - 'artifacts': ['my_app/lib.so'], - 'hooks': {'custom': {'path': DEFAULT_BUILD_SCRIPT}}, + "project": {"name": project_name, "requires-python": ">3", "dynamic": ["version"]}, + "tool": { + "hatch": { + "version": {"path": "my_app/__about__.py"}, + "build": { + "targets": {"wheel": {"versions": ["standard"]}}, + "artifacts": ["my_app/lib.so"], + "hooks": {"custom": {"path": DEFAULT_BUILD_SCRIPT}}, }, }, }, } builder = WheelBuilder(str(project_path), config=config) - build_path = project_path / 'dist' + build_path = project_path / "dist" build_path.mkdir() - with project_path.as_cwd({'ARCHFLAGS': archflags}): + with project_path.as_cwd({"ARCHFLAGS": archflags}): artifacts = list(builder.build(directory=str(build_path))) assert len(artifacts) == 1 @@ -3600,18 +3603,18 @@ def initialize(self, version, build_data): tag = next(sys_tags()) tag_parts = [tag.interpreter, tag.abi, tag.platform] tag_parts[2] = tag_parts[2].replace(platform.mac_ver()[2], expected_arch) - expected_tag = '-'.join(tag_parts) - assert expected_artifact == str(build_path / f'{builder.project_id}-{expected_tag}.whl') + expected_tag = "-".join(tag_parts) + assert expected_artifact == str(build_path / f"{builder.project_id}-{expected_tag}.whl") - extraction_directory = temp_dir / '_archive' + extraction_directory = temp_dir / "_archive" extraction_directory.mkdir() - with zipfile.ZipFile(str(expected_artifact), 'r') as zip_archive: + with zipfile.ZipFile(str(expected_artifact), "r") as zip_archive: zip_archive.extractall(str(extraction_directory)) - metadata_directory = f'{builder.project_id}.dist-info' + metadata_directory = f"{builder.project_id}.dist-info" expected_files = helpers.get_template_files( - 'wheel.standard_default_build_script_artifacts', + "wheel.standard_default_build_script_artifacts", project_name, metadata_directory=metadata_directory, tag=expected_tag, @@ -3619,22 +3622,22 @@ def initialize(self, version, build_data): helpers.assert_files(extraction_directory, expected_files) @pytest.mark.requires_macos - @pytest.mark.parametrize('macos_max_compat', [True, False]) + @pytest.mark.parametrize("macos_max_compat", [True, False]) def test_macos_max_compat(self, hatch, helpers, temp_dir, config_file, macos_max_compat): - config_file.model.template.plugins['default']['src-layout'] = False + config_file.model.template.plugins["default"]["src-layout"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' + project_path = temp_dir / "my-app" - vcs_ignore_file = project_path / '.gitignore' - vcs_ignore_file.write_text('*.pyc\n*.so\n*.h') + vcs_ignore_file = project_path / ".gitignore" + vcs_ignore_file.write_text("*.pyc\n*.so\n*.h") build_script = project_path / DEFAULT_BUILD_SCRIPT build_script.write_text( @@ -3656,21 +3659,21 @@ def initialize(self, version, build_data): ) config = { - 'project': {'name': project_name, 'requires-python': '>3', 'dynamic': ['version']}, - 'tool': { - 'hatch': { - 'version': {'path': 'my_app/__about__.py'}, - 'build': { - 'targets': {'wheel': {'versions': ['standard'], 'macos-max-compat': macos_max_compat}}, - 'artifacts': ['my_app/lib.so'], - 'hooks': {'custom': {'path': DEFAULT_BUILD_SCRIPT}}, + "project": {"name": project_name, "requires-python": ">3", "dynamic": ["version"]}, + "tool": { + "hatch": { + "version": {"path": "my_app/__about__.py"}, + "build": { + "targets": {"wheel": {"versions": ["standard"], "macos-max-compat": macos_max_compat}}, + "artifacts": ["my_app/lib.so"], + "hooks": {"custom": {"path": DEFAULT_BUILD_SCRIPT}}, }, }, }, } builder = WheelBuilder(str(project_path), config=config) - build_path = project_path / 'dist' + build_path = project_path / "dist" build_path.mkdir() with project_path.as_cwd(): @@ -3686,22 +3689,22 @@ def initialize(self, version, build_data): tag = next(sys_tags()) tag_parts = [tag.interpreter, tag.abi, tag.platform] if macos_max_compat: - sdk_version_major, sdk_version_minor = tag_parts[2].split('_')[1:3] + sdk_version_major, sdk_version_minor = tag_parts[2].split("_")[1:3] if int(sdk_version_major) >= 11: - tag_parts[2] = tag_parts[2].replace(f'{sdk_version_major}_{sdk_version_minor}', '10_16', 1) + tag_parts[2] = tag_parts[2].replace(f"{sdk_version_major}_{sdk_version_minor}", "10_16", 1) - expected_tag = '-'.join(tag_parts) - assert expected_artifact == str(build_path / f'{builder.project_id}-{expected_tag}.whl') + expected_tag = "-".join(tag_parts) + assert expected_artifact == str(build_path / f"{builder.project_id}-{expected_tag}.whl") - extraction_directory = temp_dir / '_archive' + extraction_directory = temp_dir / "_archive" extraction_directory.mkdir() - with zipfile.ZipFile(str(expected_artifact), 'r') as zip_archive: + with zipfile.ZipFile(str(expected_artifact), "r") as zip_archive: zip_archive.extractall(str(extraction_directory)) - metadata_directory = f'{builder.project_id}.dist-info' + metadata_directory = f"{builder.project_id}.dist-info" expected_files = helpers.get_template_files( - 'wheel.standard_default_build_script_artifacts', + "wheel.standard_default_build_script_artifacts", project_name, metadata_directory=metadata_directory, tag=expected_tag, @@ -3709,30 +3712,30 @@ def initialize(self, version, build_data): helpers.assert_files(extraction_directory, expected_files) def test_file_permissions_normalized(self, hatch, temp_dir, config_file): - config_file.model.template.plugins['default']['src-layout'] = False + config_file.model.template.plugins["default"]["src-layout"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' + project_path = temp_dir / "my-app" config = { - 'project': {'name': project_name, 'dynamic': ['version']}, - 'tool': { - 'hatch': { - 'version': {'path': 'my_app/__about__.py'}, - 'build': {'targets': {'wheel': {'versions': ['standard'], 'strict-naming': False}}}, + "project": {"name": project_name, "dynamic": ["version"]}, + "tool": { + "hatch": { + "version": {"path": "my_app/__about__.py"}, + "build": {"targets": {"wheel": {"versions": ["standard"], "strict-naming": False}}}, }, }, } builder = WheelBuilder(str(project_path), config=config) - build_path = project_path / 'dist' + build_path = project_path / "dist" with project_path.as_cwd(): artifacts = list(builder.build()) @@ -3744,7 +3747,7 @@ def test_file_permissions_normalized(self, hatch, temp_dir, config_file): assert len(build_artifacts) == 1 assert expected_artifact == str(build_artifacts[0]) assert expected_artifact == str( - build_path / f'{builder.artifact_project_id}-{get_python_versions_tag()}-none-any.whl' + build_path / f"{builder.artifact_project_id}-{get_python_versions_tag()}-none-any.whl" ) file_stat = os.stat(expected_artifact) # we assert that at minimum 644 is set, based on the platform (e.g.) diff --git a/tests/backend/metadata/test_build.py b/tests/backend/metadata/test_build.py index 94945e510..32f0435c9 100644 --- a/tests/backend/metadata/test_build.py +++ b/tests/backend/metadata/test_build.py @@ -11,30 +11,30 @@ def test_default(self, isolation): assert metadata.requires == metadata.requires == [] def test_not_array(self, isolation): - metadata = BuildMetadata(str(isolation), {'requires': 10}) + metadata = BuildMetadata(str(isolation), {"requires": 10}) - with pytest.raises(TypeError, match='Field `build-system.requires` must be an array'): + with pytest.raises(TypeError, match="Field `build-system.requires` must be an array"): _ = metadata.requires def test_entry_not_string(self, isolation): - metadata = BuildMetadata(str(isolation), {'requires': [10]}) + metadata = BuildMetadata(str(isolation), {"requires": [10]}) - with pytest.raises(TypeError, match='Dependency #1 of field `build-system.requires` must be a string'): + with pytest.raises(TypeError, match="Dependency #1 of field `build-system.requires` must be a string"): _ = metadata.requires def test_invalid_specifier(self, isolation): - metadata = BuildMetadata(str(isolation), {'requires': ['foo^1']}) + metadata = BuildMetadata(str(isolation), {"requires": ["foo^1"]}) - with pytest.raises(ValueError, match='Dependency #1 of field `build-system.requires` is invalid: .+'): + with pytest.raises(ValueError, match="Dependency #1 of field `build-system.requires` is invalid: .+"): _ = metadata.requires def test_correct(self, isolation): - metadata = BuildMetadata(str(isolation), {'requires': ['foo', 'bar', 'Baz']}) + metadata = BuildMetadata(str(isolation), {"requires": ["foo", "bar", "Baz"]}) - assert metadata.requires == metadata.requires == ['foo', 'bar', 'Baz'] + assert metadata.requires == metadata.requires == ["foo", "bar", "Baz"] def test_correct_complex_type(self, isolation): - metadata = BuildMetadata(str(isolation), {'requires': ['foo']}) + metadata = BuildMetadata(str(isolation), {"requires": ["foo"]}) assert isinstance(metadata.requires_complex, list) assert isinstance(metadata.requires_complex[0], Requirement) @@ -44,18 +44,18 @@ class TestBuildBackend: def test_default(self, isolation): metadata = BuildMetadata(str(isolation), {}) - assert metadata.build_backend == metadata.build_backend == '' + assert metadata.build_backend == metadata.build_backend == "" def test_not_string(self, isolation): - metadata = BuildMetadata(str(isolation), {'build-backend': 10}) + metadata = BuildMetadata(str(isolation), {"build-backend": 10}) - with pytest.raises(TypeError, match='Field `build-system.build-backend` must be a string'): + with pytest.raises(TypeError, match="Field `build-system.build-backend` must be a string"): _ = metadata.build_backend def test_correct(self, isolation): - metadata = BuildMetadata(str(isolation), {'build-backend': 'foo'}) + metadata = BuildMetadata(str(isolation), {"build-backend": "foo"}) - assert metadata.build_backend == metadata.build_backend == 'foo' + assert metadata.build_backend == metadata.build_backend == "foo" class TestBackendPath: @@ -65,18 +65,18 @@ def test_default(self, isolation): assert metadata.backend_path == metadata.backend_path == [] def test_not_array(self, isolation): - metadata = BuildMetadata(str(isolation), {'backend-path': 10}) + metadata = BuildMetadata(str(isolation), {"backend-path": 10}) - with pytest.raises(TypeError, match='Field `build-system.backend-path` must be an array'): + with pytest.raises(TypeError, match="Field `build-system.backend-path` must be an array"): _ = metadata.backend_path def test_entry_not_string(self, isolation): - metadata = BuildMetadata(str(isolation), {'backend-path': [10]}) + metadata = BuildMetadata(str(isolation), {"backend-path": [10]}) - with pytest.raises(TypeError, match='Entry #1 of field `build-system.backend-path` must be a string'): + with pytest.raises(TypeError, match="Entry #1 of field `build-system.backend-path` must be a string"): _ = metadata.backend_path def test_correct(self, isolation): - metadata = BuildMetadata(str(isolation), {'backend-path': ['foo', 'bar', 'Baz']}) + metadata = BuildMetadata(str(isolation), {"backend-path": ["foo", "bar", "Baz"]}) - assert metadata.backend_path == metadata.backend_path == ['foo', 'bar', 'Baz'] + assert metadata.backend_path == metadata.backend_path == ["foo", "bar", "Baz"] diff --git a/tests/backend/metadata/test_core.py b/tests/backend/metadata/test_core.py index 2795c694d..a51fe3cb8 100644 --- a/tests/backend/metadata/test_core.py +++ b/tests/backend/metadata/test_core.py @@ -11,7 +11,7 @@ from hatchling.version.source.regex import RegexSource -@pytest.fixture(scope='module') +@pytest.fixture(scope="module") def latest_spec(): return get_core_metadata_constructors()[LATEST_METADATA_VERSION] @@ -29,18 +29,18 @@ def test_reuse(self, isolation): assert metadata.config is metadata.config is config def test_read(self, temp_dir): - project_file = temp_dir / 'pyproject.toml' - project_file.write_text('foo = 5') + project_file = temp_dir / "pyproject.toml" + project_file.write_text("foo = 5") with temp_dir.as_cwd(): metadata = ProjectMetadata(str(temp_dir), None) - assert metadata.config == metadata.config == {'foo': 5} + assert metadata.config == metadata.config == {"foo": 5} class TestInterface: def test_types(self, isolation): - metadata = ProjectMetadata(str(isolation), None, {'project': {}}) + metadata = ProjectMetadata(str(isolation), None, {"project": {}}) assert isinstance(metadata.core, CoreMetadata) assert isinstance(metadata.hatch, HatchMetadata) @@ -49,63 +49,63 @@ def test_types(self, isolation): def test_missing_core_metadata(self, isolation): metadata = ProjectMetadata(str(isolation), None, {}) - with pytest.raises(ValueError, match='Missing `project` metadata table in configuration'): + with pytest.raises(ValueError, match="Missing `project` metadata table in configuration"): _ = metadata.core def test_core_metadata_not_table(self, isolation): - metadata = ProjectMetadata(str(isolation), None, {'project': 'foo'}) + metadata = ProjectMetadata(str(isolation), None, {"project": "foo"}) - with pytest.raises(TypeError, match='The `project` configuration must be a table'): + with pytest.raises(TypeError, match="The `project` configuration must be a table"): _ = metadata.core def test_tool_metadata_not_table(self, isolation): - metadata = ProjectMetadata(str(isolation), None, {'tool': 'foo'}) + metadata = ProjectMetadata(str(isolation), None, {"tool": "foo"}) - with pytest.raises(TypeError, match='The `tool` configuration must be a table'): + with pytest.raises(TypeError, match="The `tool` configuration must be a table"): _ = metadata.hatch def test_hatch_metadata_not_table(self, isolation): - metadata = ProjectMetadata(str(isolation), None, {'tool': {'hatch': 'foo'}}) + metadata = ProjectMetadata(str(isolation), None, {"tool": {"hatch": "foo"}}) - with pytest.raises(TypeError, match='The `tool.hatch` configuration must be a table'): + with pytest.raises(TypeError, match="The `tool.hatch` configuration must be a table"): _ = metadata.hatch def test_build_metadata_not_table(self, isolation): - metadata = ProjectMetadata(str(isolation), None, {'build-system': 'foo'}) + metadata = ProjectMetadata(str(isolation), None, {"build-system": "foo"}) - with pytest.raises(TypeError, match='The `build-system` configuration must be a table'): + with pytest.raises(TypeError, match="The `build-system` configuration must be a table"): _ = metadata.build class TestDynamic: def test_not_array(self, isolation): - metadata = ProjectMetadata(str(isolation), None, {'project': {'dynamic': 10}}) + metadata = ProjectMetadata(str(isolation), None, {"project": {"dynamic": 10}}) - with pytest.raises(TypeError, match='Field `project.dynamic` must be an array'): + with pytest.raises(TypeError, match="Field `project.dynamic` must be an array"): _ = metadata.core.dynamic def test_entry_not_string(self, isolation): - metadata = ProjectMetadata(str(isolation), None, {'project': {'dynamic': [10]}}) + metadata = ProjectMetadata(str(isolation), None, {"project": {"dynamic": [10]}}) - with pytest.raises(TypeError, match='Field #1 of field `project.dynamic` must be a string'): + with pytest.raises(TypeError, match="Field #1 of field `project.dynamic` must be a string"): _ = metadata.core.dynamic def test_correct(self, isolation): - dynamic = ['version'] - metadata = ProjectMetadata(str(isolation), None, {'project': {'dynamic': dynamic}}) + dynamic = ["version"] + metadata = ProjectMetadata(str(isolation), None, {"project": {"dynamic": dynamic}}) - assert metadata.core.dynamic == ['version'] + assert metadata.core.dynamic == ["version"] def test_cache_not_array(self, isolation): - metadata = ProjectMetadata(str(isolation), None, {'project': {'dynamic': 10}}) + metadata = ProjectMetadata(str(isolation), None, {"project": {"dynamic": 10}}) - with pytest.raises(TypeError, match='Field `project.dynamic` must be an array'): + with pytest.raises(TypeError, match="Field `project.dynamic` must be an array"): _ = metadata.dynamic def test_cache_entry_not_string(self, isolation): - metadata = ProjectMetadata(str(isolation), None, {'project': {'dynamic': [10]}}) + metadata = ProjectMetadata(str(isolation), None, {"project": {"dynamic": [10]}}) - with pytest.raises(TypeError, match='Field #1 of field `project.dynamic` must be a string'): + with pytest.raises(TypeError, match="Field #1 of field `project.dynamic` must be a string"): _ = metadata.dynamic def test_cache_correct(self, temp_dir, helpers): @@ -113,12 +113,12 @@ def test_cache_correct(self, temp_dir, helpers): str(temp_dir), PluginManager(), { - 'project': {'name': 'foo', 'dynamic': ['version', 'description']}, - 'tool': {'hatch': {'version': {'path': 'a/b'}, 'metadata': {'hooks': {'custom': {}}}}}, + "project": {"name": "foo", "dynamic": ["version", "description"]}, + "tool": {"hatch": {"version": {"path": "a/b"}, "metadata": {"hooks": {"custom": {}}}}}, }, ) - file_path = temp_dir / 'a' / 'b' + file_path = temp_dir / "a" / "b" file_path.ensure_parent_dir_exists() file_path.write_text('__version__ = "0.0.1"') @@ -137,167 +137,167 @@ def update(self, metadata): # Trigger hooks with `metadata.core` first assert metadata.core.dynamic == [] - assert metadata.dynamic == ['version', 'description'] + assert metadata.dynamic == ["version", "description"] class TestRawName: def test_dynamic(self, isolation): - metadata = ProjectMetadata(str(isolation), None, {'project': {'name': 9000, 'dynamic': ['name']}}) + metadata = ProjectMetadata(str(isolation), None, {"project": {"name": 9000, "dynamic": ["name"]}}) with pytest.raises( - ValueError, match='Static metadata field `name` cannot be present in field `project.dynamic`' + ValueError, match="Static metadata field `name` cannot be present in field `project.dynamic`" ): _ = metadata.core.raw_name def test_missing(self, isolation): - metadata = ProjectMetadata(str(isolation), None, {'project': {}}) + metadata = ProjectMetadata(str(isolation), None, {"project": {}}) - with pytest.raises(ValueError, match='Missing required field `project.name`'): + with pytest.raises(ValueError, match="Missing required field `project.name`"): _ = metadata.core.raw_name def test_not_string(self, isolation): - metadata = ProjectMetadata(str(isolation), None, {'project': {'name': 9000}}) + metadata = ProjectMetadata(str(isolation), None, {"project": {"name": 9000}}) - with pytest.raises(TypeError, match='Field `project.name` must be a string'): + with pytest.raises(TypeError, match="Field `project.name` must be a string"): _ = metadata.core.raw_name def test_invalid(self, isolation): - metadata = ProjectMetadata(str(isolation), None, {'project': {'name': 'my app'}}) + metadata = ProjectMetadata(str(isolation), None, {"project": {"name": "my app"}}) with pytest.raises( ValueError, match=( - 'Required field `project.name` must only contain ASCII letters/digits, underscores, ' - 'hyphens, and periods, and must begin and end with ASCII letters/digits.' + "Required field `project.name` must only contain ASCII letters/digits, underscores, " + "hyphens, and periods, and must begin and end with ASCII letters/digits." ), ): _ = metadata.core.raw_name def test_correct(self, isolation): - name = 'My.App' - metadata = ProjectMetadata(str(isolation), None, {'project': {'name': name}}) + name = "My.App" + metadata = ProjectMetadata(str(isolation), None, {"project": {"name": name}}) assert metadata.core.raw_name is metadata.core.raw_name is name class TestName: - @pytest.mark.parametrize('name', ['My--App', 'My__App', 'My..App']) + @pytest.mark.parametrize("name", ["My--App", "My__App", "My..App"]) def test_normalization(self, isolation, name): - metadata = ProjectMetadata(str(isolation), None, {'project': {'name': name}}) + metadata = ProjectMetadata(str(isolation), None, {"project": {"name": name}}) - assert metadata.core.name == metadata.core.name == 'my-app' + assert metadata.core.name == metadata.core.name == "my-app" class TestVersion: def test_dynamic(self, isolation): - metadata = ProjectMetadata(str(isolation), None, {'project': {'version': 9000, 'dynamic': ['version']}}) + metadata = ProjectMetadata(str(isolation), None, {"project": {"version": 9000, "dynamic": ["version"]}}) with pytest.raises( ValueError, - match='Metadata field `version` cannot be both statically defined and listed in field `project.dynamic`', + match="Metadata field `version` cannot be both statically defined and listed in field `project.dynamic`", ): _ = metadata.core.version def test_static_missing(self, isolation): - metadata = ProjectMetadata(str(isolation), None, {'project': {}}) + metadata = ProjectMetadata(str(isolation), None, {"project": {}}) with pytest.raises( ValueError, - match='Field `project.version` can only be resolved dynamically if `version` is in field `project.dynamic`', + match="Field `project.version` can only be resolved dynamically if `version` is in field `project.dynamic`", ): _ = metadata.version def test_static_not_string(self, isolation): - metadata = ProjectMetadata(str(isolation), None, {'project': {'version': 9000}}) + metadata = ProjectMetadata(str(isolation), None, {"project": {"version": 9000}}) - with pytest.raises(TypeError, match='Field `project.version` must be a string'): + with pytest.raises(TypeError, match="Field `project.version` must be a string"): _ = metadata.version def test_static_invalid(self, isolation): - metadata = ProjectMetadata(str(isolation), None, {'project': {'version': '0..0'}}) + metadata = ProjectMetadata(str(isolation), None, {"project": {"version": "0..0"}}) with pytest.raises( ValueError, - match='Invalid version `0..0` from field `project.version`, see https://peps.python.org/pep-0440/', + match="Invalid version `0..0` from field `project.version`, see https://peps.python.org/pep-0440/", ): _ = metadata.version def test_static_normalization(self, isolation): - metadata = ProjectMetadata(str(isolation), None, {'project': {'version': '0.1.0.0-rc.1'}}) + metadata = ProjectMetadata(str(isolation), None, {"project": {"version": "0.1.0.0-rc.1"}}) - assert metadata.version == metadata.version == '0.1.0.0rc1' - assert metadata.core.version == metadata.core.version == '0.1.0.0-rc.1' + assert metadata.version == metadata.version == "0.1.0.0rc1" + assert metadata.core.version == metadata.core.version == "0.1.0.0-rc.1" def test_dynamic_missing(self, isolation): - metadata = ProjectMetadata(str(isolation), None, {'project': {'dynamic': ['version']}, 'tool': {'hatch': {}}}) + metadata = ProjectMetadata(str(isolation), None, {"project": {"dynamic": ["version"]}, "tool": {"hatch": {}}}) - with pytest.raises(ValueError, match='Missing `tool.hatch.version` configuration'): + with pytest.raises(ValueError, match="Missing `tool.hatch.version` configuration"): _ = metadata.version def test_dynamic_not_table(self, isolation): metadata = ProjectMetadata( - str(isolation), None, {'project': {'dynamic': ['version']}, 'tool': {'hatch': {'version': '1.0'}}} + str(isolation), None, {"project": {"dynamic": ["version"]}, "tool": {"hatch": {"version": "1.0"}}} ) - with pytest.raises(TypeError, match='Field `tool.hatch.version` must be a table'): + with pytest.raises(TypeError, match="Field `tool.hatch.version` must be a table"): _ = metadata.version def test_dynamic_source_empty(self, isolation): metadata = ProjectMetadata( - str(isolation), None, {'project': {'dynamic': ['version']}, 'tool': {'hatch': {'version': {'source': ''}}}} + str(isolation), None, {"project": {"dynamic": ["version"]}, "tool": {"hatch": {"version": {"source": ""}}}} ) with pytest.raises( - ValueError, match='The `source` option under the `tool.hatch.version` table must not be empty if defined' + ValueError, match="The `source` option under the `tool.hatch.version` table must not be empty if defined" ): _ = metadata.version.cached def test_dynamic_source_not_string(self, isolation): metadata = ProjectMetadata( - str(isolation), None, {'project': {'dynamic': ['version']}, 'tool': {'hatch': {'version': {'source': 42}}}} + str(isolation), None, {"project": {"dynamic": ["version"]}, "tool": {"hatch": {"version": {"source": 42}}}} ) - with pytest.raises(TypeError, match='Field `tool.hatch.version.source` must be a string'): + with pytest.raises(TypeError, match="Field `tool.hatch.version.source` must be a string"): _ = metadata.version.cached def test_dynamic_unknown_source(self, isolation): metadata = ProjectMetadata( str(isolation), PluginManager(), - {'project': {'dynamic': ['version']}, 'tool': {'hatch': {'version': {'source': 'foo'}}}}, + {"project": {"dynamic": ["version"]}, "tool": {"hatch": {"version": {"source": "foo"}}}}, ) - with pytest.raises(ValueError, match='Unknown version source: foo'): + with pytest.raises(ValueError, match="Unknown version source: foo"): _ = metadata.version.cached def test_dynamic_source_regex(self, temp_dir): metadata = ProjectMetadata( str(temp_dir), PluginManager(), - {'project': {'dynamic': ['version']}, 'tool': {'hatch': {'version': {'source': 'regex', 'path': 'a/b'}}}}, + {"project": {"dynamic": ["version"]}, "tool": {"hatch": {"version": {"source": "regex", "path": "a/b"}}}}, ) - file_path = temp_dir / 'a' / 'b' + file_path = temp_dir / "a" / "b" file_path.ensure_parent_dir_exists() file_path.write_text('__version__ = "0.0.1"') assert metadata.hatch.version.source is metadata.hatch.version.source assert isinstance(metadata.hatch.version.source, RegexSource) - assert metadata.hatch.version.cached == metadata.hatch.version.cached == '0.0.1' + assert metadata.hatch.version.cached == metadata.hatch.version.cached == "0.0.1" def test_dynamic_source_regex_invalid(self, temp_dir): metadata = ProjectMetadata( str(temp_dir), PluginManager(), - {'project': {'dynamic': ['version']}, 'tool': {'hatch': {'version': {'source': 'regex', 'path': 'a/b'}}}}, + {"project": {"dynamic": ["version"]}, "tool": {"hatch": {"version": {"source": "regex", "path": "a/b"}}}}, ) - file_path = temp_dir / 'a' / 'b' + file_path = temp_dir / "a" / "b" file_path.ensure_parent_dir_exists() file_path.write_text('__version__ = "0..0"') with pytest.raises( - ValueError, match='Invalid version `0..0` from source `regex`, see https://peps.python.org/pep-0440/' + ValueError, match="Invalid version `0..0` from source `regex`, see https://peps.python.org/pep-0440/" ): _ = metadata.version @@ -305,792 +305,791 @@ def test_dynamic_error(self, isolation): metadata = ProjectMetadata( str(isolation), PluginManager(), - {'project': {'dynamic': ['version']}, 'tool': {'hatch': {'version': {'source': 'regex'}}}}, + {"project": {"dynamic": ["version"]}, "tool": {"hatch": {"version": {"source": "regex"}}}}, ) with pytest.raises( - ValueError, match='Error getting the version from source `regex`: option `path` must be specified' + ValueError, match="Error getting the version from source `regex`: option `path` must be specified" ): _ = metadata.version.cached class TestDescription: def test_dynamic(self, isolation): - metadata = ProjectMetadata(str(isolation), None, {'project': {'description': 9000, 'dynamic': ['description']}}) + metadata = ProjectMetadata(str(isolation), None, {"project": {"description": 9000, "dynamic": ["description"]}}) with pytest.raises( ValueError, match=( - 'Metadata field `description` cannot be both statically defined and listed in field `project.dynamic`' + "Metadata field `description` cannot be both statically defined and listed in field `project.dynamic`" ), ): _ = metadata.core.description def test_not_string(self, isolation): - metadata = ProjectMetadata(str(isolation), None, {'project': {'description': 9000}}) + metadata = ProjectMetadata(str(isolation), None, {"project": {"description": 9000}}) - with pytest.raises(TypeError, match='Field `project.description` must be a string'): + with pytest.raises(TypeError, match="Field `project.description` must be a string"): _ = metadata.core.description def test_default(self, isolation): - metadata = ProjectMetadata(str(isolation), None, {'project': {}}) + metadata = ProjectMetadata(str(isolation), None, {"project": {}}) - assert metadata.core.description == metadata.core.description == '' + assert metadata.core.description == metadata.core.description == "" def test_custom(self, isolation): - metadata = ProjectMetadata(str(isolation), None, {'project': {'description': 'foo'}}) + metadata = ProjectMetadata(str(isolation), None, {"project": {"description": "foo"}}) - assert metadata.core.description == metadata.core.description == 'foo' + assert metadata.core.description == metadata.core.description == "foo" def test_normaliza(self, isolation): - metadata = ProjectMetadata(str(isolation), None, {'project': {'description': '\nfirst line.\r\nsecond line'}}) + metadata = ProjectMetadata(str(isolation), None, {"project": {"description": "\nfirst line.\r\nsecond line"}}) - assert metadata.core.description == metadata.core.description == ' first line. second line' + assert metadata.core.description == metadata.core.description == " first line. second line" class TestReadme: def test_dynamic(self, isolation): - metadata = ProjectMetadata(str(isolation), None, {'project': {'readme': 9000, 'dynamic': ['readme']}}) + metadata = ProjectMetadata(str(isolation), None, {"project": {"readme": 9000, "dynamic": ["readme"]}}) with pytest.raises( ValueError, - match='Metadata field `readme` cannot be both statically defined and listed in field `project.dynamic`', + match="Metadata field `readme` cannot be both statically defined and listed in field `project.dynamic`", ): _ = metadata.core.readme - @pytest.mark.parametrize('attribute', ['readme', 'readme_content_type']) + @pytest.mark.parametrize("attribute", ["readme", "readme_content_type"]) def test_unknown_type(self, isolation, attribute): - metadata = ProjectMetadata(str(isolation), None, {'project': {'readme': 9000}}) + metadata = ProjectMetadata(str(isolation), None, {"project": {"readme": 9000}}) - with pytest.raises(TypeError, match='Field `project.readme` must be a string or a table'): + with pytest.raises(TypeError, match="Field `project.readme` must be a string or a table"): _ = getattr(metadata.core, attribute) def test_default(self, isolation): - metadata = ProjectMetadata(str(isolation), None, {'project': {}}) + metadata = ProjectMetadata(str(isolation), None, {"project": {}}) - assert metadata.core.readme == metadata.core.readme == '' - assert metadata.core.readme_content_type == metadata.core.readme_content_type == 'text/markdown' - assert metadata.core.readme_path == metadata.core.readme_path == '' + assert metadata.core.readme == metadata.core.readme == "" + assert metadata.core.readme_content_type == metadata.core.readme_content_type == "text/markdown" + assert metadata.core.readme_path == metadata.core.readme_path == "" def test_string_path_unknown_content_type(self, isolation): - metadata = ProjectMetadata(str(isolation), None, {'project': {'readme': 'foo'}}) + metadata = ProjectMetadata(str(isolation), None, {"project": {"readme": "foo"}}) with pytest.raises( - TypeError, match='Unable to determine the content-type based on the extension of readme file: foo' + TypeError, match="Unable to determine the content-type based on the extension of readme file: foo" ): _ = metadata.core.readme def test_string_path_nonexistent(self, isolation): - metadata = ProjectMetadata(str(isolation), None, {'project': {'readme': 'foo/bar.md'}}) + metadata = ProjectMetadata(str(isolation), None, {"project": {"readme": "foo/bar.md"}}) - with pytest.raises(OSError, match='Readme file does not exist: foo/bar\\.md'): + with pytest.raises(OSError, match="Readme file does not exist: foo/bar\\.md"): _ = metadata.core.readme @pytest.mark.parametrize( - ('extension', 'content_type'), [('.md', 'text/markdown'), ('.rst', 'text/x-rst'), ('.txt', 'text/plain')] + ("extension", "content_type"), [(".md", "text/markdown"), (".rst", "text/x-rst"), (".txt", "text/plain")] ) def test_string_correct(self, extension, content_type, temp_dir): - metadata = ProjectMetadata(str(temp_dir), None, {'project': {'readme': f'foo/bar{extension}'}}) + metadata = ProjectMetadata(str(temp_dir), None, {"project": {"readme": f"foo/bar{extension}"}}) - file_path = temp_dir / 'foo' / f'bar{extension}' + file_path = temp_dir / "foo" / f"bar{extension}" file_path.ensure_parent_dir_exists() - file_path.write_text('test content') + file_path.write_text("test content") - assert metadata.core.readme == metadata.core.readme == 'test content' + assert metadata.core.readme == metadata.core.readme == "test content" assert metadata.core.readme_content_type == metadata.core.readme_content_type == content_type - assert metadata.core.readme_path == metadata.core.readme_path == f'foo/bar{extension}' + assert metadata.core.readme_path == metadata.core.readme_path == f"foo/bar{extension}" def test_table_content_type_missing(self, isolation): - metadata = ProjectMetadata(str(isolation), None, {'project': {'readme': {}}}) + metadata = ProjectMetadata(str(isolation), None, {"project": {"readme": {}}}) - with pytest.raises(ValueError, match='Field `content-type` is required in the `project.readme` table'): + with pytest.raises(ValueError, match="Field `content-type` is required in the `project.readme` table"): _ = metadata.core.readme def test_table_content_type_not_string(self, isolation): - metadata = ProjectMetadata(str(isolation), None, {'project': {'readme': {'content-type': 5}}}) + metadata = ProjectMetadata(str(isolation), None, {"project": {"readme": {"content-type": 5}}}) - with pytest.raises(TypeError, match='Field `content-type` in the `project.readme` table must be a string'): + with pytest.raises(TypeError, match="Field `content-type` in the `project.readme` table must be a string"): _ = metadata.core.readme def test_table_content_type_not_unknown(self, isolation): - metadata = ProjectMetadata(str(isolation), None, {'project': {'readme': {'content-type': 'foo'}}}) + metadata = ProjectMetadata(str(isolation), None, {"project": {"readme": {"content-type": "foo"}}}) with pytest.raises( ValueError, match=( - 'Field `content-type` in the `project.readme` table must be one of the following: ' - 'text/markdown, text/x-rst, text/plain' + "Field `content-type` in the `project.readme` table must be one of the following: " + "text/markdown, text/x-rst, text/plain" ), ): _ = metadata.core.readme def test_table_multiple_options(self, isolation): metadata = ProjectMetadata( - str(isolation), None, {'project': {'readme': {'content-type': 'text/markdown', 'file': '', 'text': ''}}} + str(isolation), None, {"project": {"readme": {"content-type": "text/markdown", "file": "", "text": ""}}} ) - with pytest.raises(ValueError, match='Cannot specify both `file` and `text` in the `project.readme` table'): + with pytest.raises(ValueError, match="Cannot specify both `file` and `text` in the `project.readme` table"): _ = metadata.core.readme def test_table_no_option(self, isolation): - metadata = ProjectMetadata(str(isolation), None, {'project': {'readme': {'content-type': 'text/markdown'}}}) + metadata = ProjectMetadata(str(isolation), None, {"project": {"readme": {"content-type": "text/markdown"}}}) - with pytest.raises(ValueError, match='Must specify either `file` or `text` in the `project.readme` table'): + with pytest.raises(ValueError, match="Must specify either `file` or `text` in the `project.readme` table"): _ = metadata.core.readme def test_table_file_not_string(self, isolation): metadata = ProjectMetadata( - str(isolation), None, {'project': {'readme': {'content-type': 'text/markdown', 'file': 4}}} + str(isolation), None, {"project": {"readme": {"content-type": "text/markdown", "file": 4}}} ) - with pytest.raises(TypeError, match='Field `file` in the `project.readme` table must be a string'): + with pytest.raises(TypeError, match="Field `file` in the `project.readme` table must be a string"): _ = metadata.core.readme def test_table_file_nonexistent(self, isolation): metadata = ProjectMetadata( - str(isolation), None, {'project': {'readme': {'content-type': 'text/markdown', 'file': 'foo/bar.md'}}} + str(isolation), None, {"project": {"readme": {"content-type": "text/markdown", "file": "foo/bar.md"}}} ) - with pytest.raises(OSError, match='Readme file does not exist: foo/bar\\.md'): + with pytest.raises(OSError, match="Readme file does not exist: foo/bar\\.md"): _ = metadata.core.readme def test_table_file_correct(self, temp_dir): metadata = ProjectMetadata( - str(temp_dir), None, {'project': {'readme': {'content-type': 'text/markdown', 'file': 'foo/bar.markdown'}}} + str(temp_dir), None, {"project": {"readme": {"content-type": "text/markdown", "file": "foo/bar.markdown"}}} ) - file_path = temp_dir / 'foo' / 'bar.markdown' + file_path = temp_dir / "foo" / "bar.markdown" file_path.ensure_parent_dir_exists() - file_path.write_text('test content') + file_path.write_text("test content") - assert metadata.core.readme == metadata.core.readme == 'test content' - assert metadata.core.readme_content_type == metadata.core.readme_content_type == 'text/markdown' - assert metadata.core.readme_path == metadata.core.readme_path == 'foo/bar.markdown' + assert metadata.core.readme == metadata.core.readme == "test content" + assert metadata.core.readme_content_type == metadata.core.readme_content_type == "text/markdown" + assert metadata.core.readme_path == metadata.core.readme_path == "foo/bar.markdown" def test_table_text_not_string(self, isolation): metadata = ProjectMetadata( - str(isolation), None, {'project': {'readme': {'content-type': 'text/markdown', 'text': 4}}} + str(isolation), None, {"project": {"readme": {"content-type": "text/markdown", "text": 4}}} ) - with pytest.raises(TypeError, match='Field `text` in the `project.readme` table must be a string'): + with pytest.raises(TypeError, match="Field `text` in the `project.readme` table must be a string"): _ = metadata.core.readme def test_table_text_correct(self, isolation): metadata = ProjectMetadata( - str(isolation), None, {'project': {'readme': {'content-type': 'text/markdown', 'text': 'test content'}}} + str(isolation), None, {"project": {"readme": {"content-type": "text/markdown", "text": "test content"}}} ) - assert metadata.core.readme == metadata.core.readme == 'test content' - assert metadata.core.readme_content_type == metadata.core.readme_content_type == 'text/markdown' - assert metadata.core.readme_path == metadata.core.readme_path == '' + assert metadata.core.readme == metadata.core.readme == "test content" + assert metadata.core.readme_content_type == metadata.core.readme_content_type == "text/markdown" + assert metadata.core.readme_path == metadata.core.readme_path == "" class TestRequiresPython: def test_dynamic(self, isolation): metadata = ProjectMetadata( - str(isolation), None, {'project': {'requires-python': 9000, 'dynamic': ['requires-python']}} + str(isolation), None, {"project": {"requires-python": 9000, "dynamic": ["requires-python"]}} ) with pytest.raises( ValueError, match=( - 'Metadata field `requires-python` cannot be both statically defined and ' - 'listed in field `project.dynamic`' + "Metadata field `requires-python` cannot be both statically defined and " + "listed in field `project.dynamic`" ), ): _ = metadata.core.requires_python - @pytest.mark.parametrize('attribute', ['requires_python', 'python_constraint']) + @pytest.mark.parametrize("attribute", ["requires_python", "python_constraint"]) def test_not_string(self, isolation, attribute): - metadata = ProjectMetadata(str(isolation), None, {'project': {'requires-python': 9000}}) + metadata = ProjectMetadata(str(isolation), None, {"project": {"requires-python": 9000}}) - with pytest.raises(TypeError, match='Field `project.requires-python` must be a string'): + with pytest.raises(TypeError, match="Field `project.requires-python` must be a string"): _ = getattr(metadata.core, attribute) def test_invalid(self, isolation): - metadata = ProjectMetadata(str(isolation), None, {'project': {'requires-python': '^1'}}) + metadata = ProjectMetadata(str(isolation), None, {"project": {"requires-python": "^1"}}) - with pytest.raises(ValueError, match='Field `project.requires-python` is invalid: .+'): + with pytest.raises(ValueError, match="Field `project.requires-python` is invalid: .+"): _ = metadata.core.requires_python def test_default(self, isolation): - metadata = ProjectMetadata(str(isolation), None, {'project': {}}) + metadata = ProjectMetadata(str(isolation), None, {"project": {}}) - assert metadata.core.requires_python == metadata.core.requires_python == '' + assert metadata.core.requires_python == metadata.core.requires_python == "" for major_version in map(str, range(10)): assert metadata.core.python_constraint.contains(major_version) def test_custom(self, isolation): - metadata = ProjectMetadata(str(isolation), None, {'project': {'requires-python': '>2'}}) + metadata = ProjectMetadata(str(isolation), None, {"project": {"requires-python": ">2"}}) - assert metadata.core.requires_python == metadata.core.requires_python == '>2' - assert not metadata.core.python_constraint.contains('2') - assert metadata.core.python_constraint.contains('3') + assert metadata.core.requires_python == metadata.core.requires_python == ">2" + assert not metadata.core.python_constraint.contains("2") + assert metadata.core.python_constraint.contains("3") class TestLicense: def test_dynamic(self, isolation): - metadata = ProjectMetadata(str(isolation), None, {'project': {'license': 9000, 'dynamic': ['license']}}) + metadata = ProjectMetadata(str(isolation), None, {"project": {"license": 9000, "dynamic": ["license"]}}) with pytest.raises( ValueError, - match='Metadata field `license` cannot be both statically defined and listed in field `project.dynamic`', + match="Metadata field `license` cannot be both statically defined and listed in field `project.dynamic`", ): _ = metadata.core.license def test_invalid_type(self, isolation): - metadata = ProjectMetadata(str(isolation), None, {'project': {'license': 9000}}) + metadata = ProjectMetadata(str(isolation), None, {"project": {"license": 9000}}) - with pytest.raises(TypeError, match='Field `project.license` must be a string or a table'): + with pytest.raises(TypeError, match="Field `project.license` must be a string or a table"): _ = metadata.core.license def test_default(self, isolation): - metadata = ProjectMetadata(str(isolation), None, {'project': {}}) + metadata = ProjectMetadata(str(isolation), None, {"project": {}}) - assert metadata.core.license == metadata.core.license == '' - assert metadata.core.license_expression == metadata.core.license_expression == '' + assert metadata.core.license == metadata.core.license == "" + assert metadata.core.license_expression == metadata.core.license_expression == "" def test_normalization(self, isolation): - metadata = ProjectMetadata(str(isolation), None, {'project': {'license': 'mit or apache-2.0'}}) + metadata = ProjectMetadata(str(isolation), None, {"project": {"license": "mit or apache-2.0"}}) - assert metadata.core.license_expression == 'MIT OR Apache-2.0' + assert metadata.core.license_expression == "MIT OR Apache-2.0" def test_invalid_expression(self, isolation): - metadata = ProjectMetadata(str(isolation), None, {'project': {'license': 'mit or foo'}}) + metadata = ProjectMetadata(str(isolation), None, {"project": {"license": "mit or foo"}}) with pytest.raises(ValueError, match="Error parsing field `project.license` - Unknown license: 'foo'"): _ = metadata.core.license_expression def test_multiple_options(self, isolation): - metadata = ProjectMetadata(str(isolation), None, {'project': {'license': {'file': '', 'text': ''}}}) + metadata = ProjectMetadata(str(isolation), None, {"project": {"license": {"file": "", "text": ""}}}) - with pytest.raises(ValueError, match='Cannot specify both `file` and `text` in the `project.license` table'): + with pytest.raises(ValueError, match="Cannot specify both `file` and `text` in the `project.license` table"): _ = metadata.core.license def test_no_option(self, isolation): - metadata = ProjectMetadata(str(isolation), None, {'project': {'license': {}}}) + metadata = ProjectMetadata(str(isolation), None, {"project": {"license": {}}}) - with pytest.raises(ValueError, match='Must specify either `file` or `text` in the `project.license` table'): + with pytest.raises(ValueError, match="Must specify either `file` or `text` in the `project.license` table"): _ = metadata.core.license def test_file_not_string(self, isolation): - metadata = ProjectMetadata(str(isolation), None, {'project': {'license': {'file': 4}}}) + metadata = ProjectMetadata(str(isolation), None, {"project": {"license": {"file": 4}}}) - with pytest.raises(TypeError, match='Field `file` in the `project.license` table must be a string'): + with pytest.raises(TypeError, match="Field `file` in the `project.license` table must be a string"): _ = metadata.core.license def test_file_nonexistent(self, isolation): - metadata = ProjectMetadata(str(isolation), None, {'project': {'license': {'file': 'foo/bar.md'}}}) + metadata = ProjectMetadata(str(isolation), None, {"project": {"license": {"file": "foo/bar.md"}}}) - with pytest.raises(OSError, match='License file does not exist: foo/bar\\.md'): + with pytest.raises(OSError, match="License file does not exist: foo/bar\\.md"): _ = metadata.core.license def test_file_correct(self, temp_dir): - metadata = ProjectMetadata(str(temp_dir), None, {'project': {'license': {'file': 'foo/bar.md'}}}) + metadata = ProjectMetadata(str(temp_dir), None, {"project": {"license": {"file": "foo/bar.md"}}}) - file_path = temp_dir / 'foo' / 'bar.md' + file_path = temp_dir / "foo" / "bar.md" file_path.ensure_parent_dir_exists() - file_path.write_text('test content') + file_path.write_text("test content") - assert metadata.core.license == 'test content' + assert metadata.core.license == "test content" def test_text_not_string(self, isolation): - metadata = ProjectMetadata(str(isolation), None, {'project': {'license': {'text': 4}}}) + metadata = ProjectMetadata(str(isolation), None, {"project": {"license": {"text": 4}}}) - with pytest.raises(TypeError, match='Field `text` in the `project.license` table must be a string'): + with pytest.raises(TypeError, match="Field `text` in the `project.license` table must be a string"): _ = metadata.core.license def test_text_correct(self, isolation): - metadata = ProjectMetadata(str(isolation), None, {'project': {'license': {'text': 'test content'}}}) + metadata = ProjectMetadata(str(isolation), None, {"project": {"license": {"text": "test content"}}}) - assert metadata.core.license == 'test content' + assert metadata.core.license == "test content" class TestLicenseFiles: def test_dynamic(self, isolation): metadata = ProjectMetadata( - str(isolation), None, {'project': {'license-files': 9000, 'dynamic': ['license-files']}} + str(isolation), None, {"project": {"license-files": 9000, "dynamic": ["license-files"]}} ) with pytest.raises( ValueError, match=( - 'Metadata field `license-files` cannot be both statically defined and ' - 'listed in field `project.dynamic`' + "Metadata field `license-files` cannot be both statically defined and listed in field `project.dynamic`" ), ): _ = metadata.core.license_files def test_not_array(self, isolation): - metadata = ProjectMetadata(str(isolation), None, {'project': {'license-files': 9000}}) + metadata = ProjectMetadata(str(isolation), None, {"project": {"license-files": 9000}}) - with pytest.raises(TypeError, match='Field `project.license-files` must be an array'): + with pytest.raises(TypeError, match="Field `project.license-files` must be an array"): _ = metadata.core.license_files def test_entry_not_string(self, isolation): - metadata = ProjectMetadata(str(isolation), None, {'project': {'license-files': [9000]}}) + metadata = ProjectMetadata(str(isolation), None, {"project": {"license-files": [9000]}}) - with pytest.raises(TypeError, match='Entry #1 of field `project.license-files` must be a string'): + with pytest.raises(TypeError, match="Entry #1 of field `project.license-files` must be a string"): _ = metadata.core.license_files def test_default_globs_no_licenses(self, isolation): - metadata = ProjectMetadata(str(isolation), None, {'project': {}}) + metadata = ProjectMetadata(str(isolation), None, {"project": {}}) assert metadata.core.license_files == metadata.core.license_files == [] def test_default_globs_with_licenses(self, temp_dir): - metadata = ProjectMetadata(str(temp_dir), None, {'project': {}}) + metadata = ProjectMetadata(str(temp_dir), None, {"project": {}}) expected = [] - (temp_dir / 'foo').touch() + (temp_dir / "foo").touch() - for name in ('LICENSE', 'LICENCE', 'COPYING', 'NOTICE', 'AUTHORS'): + for name in ("LICENSE", "LICENCE", "COPYING", "NOTICE", "AUTHORS"): (temp_dir / name).touch() expected.append(name) - name_with_extension = f'{name}.txt' - (temp_dir / f'{name}.txt').touch() + name_with_extension = f"{name}.txt" + (temp_dir / f"{name}.txt").touch() expected.append(name_with_extension) assert metadata.core.license_files == sorted(expected) def test_globs_with_licenses(self, temp_dir): - metadata = ProjectMetadata(str(temp_dir), None, {'project': {'license-files': ['LICENSES/*']}}) + metadata = ProjectMetadata(str(temp_dir), None, {"project": {"license-files": ["LICENSES/*"]}}) - licenses_dir = temp_dir / 'LICENSES' + licenses_dir = temp_dir / "LICENSES" licenses_dir.mkdir() - (licenses_dir / 'MIT.txt').touch() - (licenses_dir / 'Apache-2.0.txt').touch() + (licenses_dir / "MIT.txt").touch() + (licenses_dir / "Apache-2.0.txt").touch() - for name in ('LICENSE', 'LICENCE', 'COPYING', 'NOTICE', 'AUTHORS'): + for name in ("LICENSE", "LICENCE", "COPYING", "NOTICE", "AUTHORS"): (temp_dir / name).touch() - assert metadata.core.license_files == ['LICENSES/Apache-2.0.txt', 'LICENSES/MIT.txt'] + assert metadata.core.license_files == ["LICENSES/Apache-2.0.txt", "LICENSES/MIT.txt"] def test_paths_with_licenses(self, temp_dir): metadata = ProjectMetadata( str(temp_dir), None, - {'project': {'license-files': ['LICENSES/Apache-2.0.txt', 'LICENSES/MIT.txt', 'COPYING']}}, + {"project": {"license-files": ["LICENSES/Apache-2.0.txt", "LICENSES/MIT.txt", "COPYING"]}}, ) - licenses_dir = temp_dir / 'LICENSES' + licenses_dir = temp_dir / "LICENSES" licenses_dir.mkdir() - (licenses_dir / 'MIT.txt').touch() - (licenses_dir / 'Apache-2.0.txt').touch() + (licenses_dir / "MIT.txt").touch() + (licenses_dir / "Apache-2.0.txt").touch() - for name in ('LICENSE', 'LICENCE', 'COPYING', 'NOTICE', 'AUTHORS'): + for name in ("LICENSE", "LICENCE", "COPYING", "NOTICE", "AUTHORS"): (temp_dir / name).touch() - assert metadata.core.license_files == ['COPYING', 'LICENSES/Apache-2.0.txt', 'LICENSES/MIT.txt'] + assert metadata.core.license_files == ["COPYING", "LICENSES/Apache-2.0.txt", "LICENSES/MIT.txt"] class TestAuthors: def test_dynamic(self, isolation): - metadata = ProjectMetadata(str(isolation), None, {'project': {'authors': 9000, 'dynamic': ['authors']}}) + metadata = ProjectMetadata(str(isolation), None, {"project": {"authors": 9000, "dynamic": ["authors"]}}) with pytest.raises( ValueError, - match='Metadata field `authors` cannot be both statically defined and listed in field `project.dynamic`', + match="Metadata field `authors` cannot be both statically defined and listed in field `project.dynamic`", ): _ = metadata.core.authors def test_not_array(self, isolation): - metadata = ProjectMetadata(str(isolation), None, {'project': {'authors': 'foo'}}) + metadata = ProjectMetadata(str(isolation), None, {"project": {"authors": "foo"}}) - with pytest.raises(TypeError, match='Field `project.authors` must be an array'): + with pytest.raises(TypeError, match="Field `project.authors` must be an array"): _ = metadata.core.authors def test_default(self, isolation): - metadata = ProjectMetadata(str(isolation), None, {'project': {}}) + metadata = ProjectMetadata(str(isolation), None, {"project": {}}) assert metadata.core.authors == metadata.core.authors == [] def test_not_table(self, isolation): - metadata = ProjectMetadata(str(isolation), None, {'project': {'authors': ['foo']}}) + metadata = ProjectMetadata(str(isolation), None, {"project": {"authors": ["foo"]}}) - with pytest.raises(TypeError, match='Author #1 of field `project.authors` must be an inline table'): + with pytest.raises(TypeError, match="Author #1 of field `project.authors` must be an inline table"): _ = metadata.core.authors def test_no_data(self, isolation): - metadata = ProjectMetadata(str(isolation), None, {'project': {'authors': [{}]}}) + metadata = ProjectMetadata(str(isolation), None, {"project": {"authors": [{}]}}) with pytest.raises( - ValueError, match='Author #1 of field `project.authors` must specify either `name` or `email`' + ValueError, match="Author #1 of field `project.authors` must specify either `name` or `email`" ): _ = metadata.core.authors def test_name_not_string(self, isolation): - metadata = ProjectMetadata(str(isolation), None, {'project': {'authors': [{'name': 9}]}}) + metadata = ProjectMetadata(str(isolation), None, {"project": {"authors": [{"name": 9}]}}) - with pytest.raises(TypeError, match='Name of author #1 of field `project.authors` must be a string'): + with pytest.raises(TypeError, match="Name of author #1 of field `project.authors` must be a string"): _ = metadata.core.authors def test_name_only(self, isolation): - metadata = ProjectMetadata(str(isolation), None, {'project': {'authors': [{'name': 'foo'}]}}) + metadata = ProjectMetadata(str(isolation), None, {"project": {"authors": [{"name": "foo"}]}}) assert len(metadata.core.authors) == 1 - assert metadata.core.authors[0] == {'name': 'foo'} - assert metadata.core.authors_data == metadata.core.authors_data == {'name': ['foo'], 'email': []} + assert metadata.core.authors[0] == {"name": "foo"} + assert metadata.core.authors_data == metadata.core.authors_data == {"name": ["foo"], "email": []} def test_email_not_string(self, isolation): - metadata = ProjectMetadata(str(isolation), None, {'project': {'authors': [{'email': 9}]}}) + metadata = ProjectMetadata(str(isolation), None, {"project": {"authors": [{"email": 9}]}}) - with pytest.raises(TypeError, match='Email of author #1 of field `project.authors` must be a string'): + with pytest.raises(TypeError, match="Email of author #1 of field `project.authors` must be a string"): _ = metadata.core.authors def test_email_only(self, isolation): - metadata = ProjectMetadata(str(isolation), None, {'project': {'authors': [{'email': 'foo@bar.baz'}]}}) + metadata = ProjectMetadata(str(isolation), None, {"project": {"authors": [{"email": "foo@bar.baz"}]}}) assert len(metadata.core.authors) == 1 - assert metadata.core.authors[0] == {'email': 'foo@bar.baz'} - assert metadata.core.authors_data == {'name': [], 'email': ['foo@bar.baz']} + assert metadata.core.authors[0] == {"email": "foo@bar.baz"} + assert metadata.core.authors_data == {"name": [], "email": ["foo@bar.baz"]} def test_name_and_email(self, isolation): metadata = ProjectMetadata( str(isolation), None, { - 'project': { - 'authors': [{'name': 'foo2', 'email': 'foo2@bar.baz'}, {'name': 'foo1', 'email': 'foo1@bar.baz'}] + "project": { + "authors": [{"name": "foo2", "email": "foo2@bar.baz"}, {"name": "foo1", "email": "foo1@bar.baz"}] } }, ) assert len(metadata.core.authors) == 2 - assert metadata.core.authors[0] == {'name': 'foo2', 'email': 'foo2@bar.baz'} - assert metadata.core.authors[1] == {'name': 'foo1', 'email': 'foo1@bar.baz'} - assert metadata.core.authors_data == {'name': [], 'email': ['foo2 ', 'foo1 ']} + assert metadata.core.authors[0] == {"name": "foo2", "email": "foo2@bar.baz"} + assert metadata.core.authors[1] == {"name": "foo1", "email": "foo1@bar.baz"} + assert metadata.core.authors_data == {"name": [], "email": ["foo2 ", "foo1 "]} class TestMaintainers: def test_dynamic(self, isolation): - metadata = ProjectMetadata(str(isolation), None, {'project': {'maintainers': 9000, 'dynamic': ['maintainers']}}) + metadata = ProjectMetadata(str(isolation), None, {"project": {"maintainers": 9000, "dynamic": ["maintainers"]}}) with pytest.raises( ValueError, match=( - 'Metadata field `maintainers` cannot be both statically defined and listed in field `project.dynamic`' + "Metadata field `maintainers` cannot be both statically defined and listed in field `project.dynamic`" ), ): _ = metadata.core.maintainers def test_not_array(self, isolation): - metadata = ProjectMetadata(str(isolation), None, {'project': {'maintainers': 'foo'}}) + metadata = ProjectMetadata(str(isolation), None, {"project": {"maintainers": "foo"}}) - with pytest.raises(TypeError, match='Field `project.maintainers` must be an array'): + with pytest.raises(TypeError, match="Field `project.maintainers` must be an array"): _ = metadata.core.maintainers def test_default(self, isolation): - metadata = ProjectMetadata(str(isolation), None, {'project': {}}) + metadata = ProjectMetadata(str(isolation), None, {"project": {}}) assert metadata.core.maintainers == metadata.core.maintainers == [] def test_not_table(self, isolation): - metadata = ProjectMetadata(str(isolation), None, {'project': {'maintainers': ['foo']}}) + metadata = ProjectMetadata(str(isolation), None, {"project": {"maintainers": ["foo"]}}) - with pytest.raises(TypeError, match='Maintainer #1 of field `project.maintainers` must be an inline table'): + with pytest.raises(TypeError, match="Maintainer #1 of field `project.maintainers` must be an inline table"): _ = metadata.core.maintainers def test_no_data(self, isolation): - metadata = ProjectMetadata(str(isolation), None, {'project': {'maintainers': [{}]}}) + metadata = ProjectMetadata(str(isolation), None, {"project": {"maintainers": [{}]}}) with pytest.raises( - ValueError, match='Maintainer #1 of field `project.maintainers` must specify either `name` or `email`' + ValueError, match="Maintainer #1 of field `project.maintainers` must specify either `name` or `email`" ): _ = metadata.core.maintainers def test_name_not_string(self, isolation): - metadata = ProjectMetadata(str(isolation), None, {'project': {'maintainers': [{'name': 9}]}}) + metadata = ProjectMetadata(str(isolation), None, {"project": {"maintainers": [{"name": 9}]}}) - with pytest.raises(TypeError, match='Name of maintainer #1 of field `project.maintainers` must be a string'): + with pytest.raises(TypeError, match="Name of maintainer #1 of field `project.maintainers` must be a string"): _ = metadata.core.maintainers def test_name_only(self, isolation): - metadata = ProjectMetadata(str(isolation), None, {'project': {'maintainers': [{'name': 'foo'}]}}) + metadata = ProjectMetadata(str(isolation), None, {"project": {"maintainers": [{"name": "foo"}]}}) assert len(metadata.core.maintainers) == 1 - assert metadata.core.maintainers[0] == {'name': 'foo'} - assert metadata.core.maintainers_data == metadata.core.maintainers_data == {'name': ['foo'], 'email': []} + assert metadata.core.maintainers[0] == {"name": "foo"} + assert metadata.core.maintainers_data == metadata.core.maintainers_data == {"name": ["foo"], "email": []} def test_email_not_string(self, isolation): - metadata = ProjectMetadata(str(isolation), None, {'project': {'maintainers': [{'email': 9}]}}) + metadata = ProjectMetadata(str(isolation), None, {"project": {"maintainers": [{"email": 9}]}}) - with pytest.raises(TypeError, match='Email of maintainer #1 of field `project.maintainers` must be a string'): + with pytest.raises(TypeError, match="Email of maintainer #1 of field `project.maintainers` must be a string"): _ = metadata.core.maintainers def test_email_only(self, isolation): - metadata = ProjectMetadata(str(isolation), None, {'project': {'maintainers': [{'email': 'foo@bar.baz'}]}}) + metadata = ProjectMetadata(str(isolation), None, {"project": {"maintainers": [{"email": "foo@bar.baz"}]}}) assert len(metadata.core.maintainers) == 1 - assert metadata.core.maintainers[0] == {'email': 'foo@bar.baz'} - assert metadata.core.maintainers_data == {'name': [], 'email': ['foo@bar.baz']} + assert metadata.core.maintainers[0] == {"email": "foo@bar.baz"} + assert metadata.core.maintainers_data == {"name": [], "email": ["foo@bar.baz"]} def test_name_and_email(self, isolation): metadata = ProjectMetadata( str(isolation), None, { - 'project': { - 'maintainers': [ - {'name': 'foo2', 'email': 'foo2@bar.baz'}, - {'name': 'foo1', 'email': 'foo1@bar.baz'}, + "project": { + "maintainers": [ + {"name": "foo2", "email": "foo2@bar.baz"}, + {"name": "foo1", "email": "foo1@bar.baz"}, ] } }, ) assert len(metadata.core.maintainers) == 2 - assert metadata.core.maintainers[0] == {'name': 'foo2', 'email': 'foo2@bar.baz'} - assert metadata.core.maintainers[1] == {'name': 'foo1', 'email': 'foo1@bar.baz'} - assert metadata.core.maintainers_data == {'name': [], 'email': ['foo2 ', 'foo1 ']} + assert metadata.core.maintainers[0] == {"name": "foo2", "email": "foo2@bar.baz"} + assert metadata.core.maintainers[1] == {"name": "foo1", "email": "foo1@bar.baz"} + assert metadata.core.maintainers_data == {"name": [], "email": ["foo2 ", "foo1 "]} class TestKeywords: def test_dynamic(self, isolation): - metadata = ProjectMetadata(str(isolation), None, {'project': {'keywords': 9000, 'dynamic': ['keywords']}}) + metadata = ProjectMetadata(str(isolation), None, {"project": {"keywords": 9000, "dynamic": ["keywords"]}}) with pytest.raises( ValueError, - match='Metadata field `keywords` cannot be both statically defined and listed in field `project.dynamic`', + match="Metadata field `keywords` cannot be both statically defined and listed in field `project.dynamic`", ): _ = metadata.core.keywords def test_not_array(self, isolation): - metadata = ProjectMetadata(str(isolation), None, {'project': {'keywords': 10}}) + metadata = ProjectMetadata(str(isolation), None, {"project": {"keywords": 10}}) - with pytest.raises(TypeError, match='Field `project.keywords` must be an array'): + with pytest.raises(TypeError, match="Field `project.keywords` must be an array"): _ = metadata.core.keywords def test_entry_not_string(self, isolation): - metadata = ProjectMetadata(str(isolation), None, {'project': {'keywords': [10]}}) + metadata = ProjectMetadata(str(isolation), None, {"project": {"keywords": [10]}}) - with pytest.raises(TypeError, match='Keyword #1 of field `project.keywords` must be a string'): + with pytest.raises(TypeError, match="Keyword #1 of field `project.keywords` must be a string"): _ = metadata.core.keywords def test_correct(self, isolation): - metadata = ProjectMetadata(str(isolation), None, {'project': {'keywords': ['foo', 'foo', 'bar']}}) + metadata = ProjectMetadata(str(isolation), None, {"project": {"keywords": ["foo", "foo", "bar"]}}) - assert metadata.core.keywords == metadata.core.keywords == ['bar', 'foo'] + assert metadata.core.keywords == metadata.core.keywords == ["bar", "foo"] class TestClassifiers: def test_dynamic(self, isolation): - metadata = ProjectMetadata(str(isolation), None, {'project': {'classifiers': 9000, 'dynamic': ['classifiers']}}) + metadata = ProjectMetadata(str(isolation), None, {"project": {"classifiers": 9000, "dynamic": ["classifiers"]}}) with pytest.raises( ValueError, match=( - 'Metadata field `classifiers` cannot be both statically defined and listed in field `project.dynamic`' + "Metadata field `classifiers` cannot be both statically defined and listed in field `project.dynamic`" ), ): _ = metadata.core.classifiers def test_not_array(self, isolation): - metadata = ProjectMetadata(str(isolation), None, {'project': {'classifiers': 10}}) + metadata = ProjectMetadata(str(isolation), None, {"project": {"classifiers": 10}}) - with pytest.raises(TypeError, match='Field `project.classifiers` must be an array'): + with pytest.raises(TypeError, match="Field `project.classifiers` must be an array"): _ = metadata.core.classifiers def test_entry_not_string(self, isolation): - metadata = ProjectMetadata(str(isolation), None, {'project': {'classifiers': [10]}}) + metadata = ProjectMetadata(str(isolation), None, {"project": {"classifiers": [10]}}) - with pytest.raises(TypeError, match='Classifier #1 of field `project.classifiers` must be a string'): + with pytest.raises(TypeError, match="Classifier #1 of field `project.classifiers` must be a string"): _ = metadata.core.classifiers def test_entry_unknown(self, isolation, monkeypatch): - monkeypatch.delenv('HATCH_METADATA_CLASSIFIERS_NO_VERIFY', False) - metadata = ProjectMetadata(str(isolation), None, {'project': {'classifiers': ['foo']}}) + monkeypatch.delenv("HATCH_METADATA_CLASSIFIERS_NO_VERIFY", False) + metadata = ProjectMetadata(str(isolation), None, {"project": {"classifiers": ["foo"]}}) - with pytest.raises(ValueError, match='Unknown classifier in field `project.classifiers`: foo'): + with pytest.raises(ValueError, match="Unknown classifier in field `project.classifiers`: foo"): _ = metadata.core.classifiers def test_entry_unknown_no_verify(self, isolation, monkeypatch): - monkeypatch.setenv('HATCH_METADATA_CLASSIFIERS_NO_VERIFY', '1') + monkeypatch.setenv("HATCH_METADATA_CLASSIFIERS_NO_VERIFY", "1") classifiers = [ - 'Programming Language :: Python :: 3.11', - 'Programming Language :: Python :: 3.11', - 'Programming Language :: Python :: 3.9', - 'Development Status :: 4 - Beta', - 'Private :: Do Not Upload', - 'Foo', + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.9", + "Development Status :: 4 - Beta", + "Private :: Do Not Upload", + "Foo", ] - metadata = ProjectMetadata(str(isolation), None, {'project': {'classifiers': classifiers}}) + metadata = ProjectMetadata(str(isolation), None, {"project": {"classifiers": classifiers}}) assert ( metadata.core.classifiers == metadata.core.classifiers == [ - 'Private :: Do Not Upload', - 'Development Status :: 4 - Beta', - 'Foo', - 'Programming Language :: Python :: 3.9', - 'Programming Language :: Python :: 3.11', + "Private :: Do Not Upload", + "Development Status :: 4 - Beta", + "Foo", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.11", ] ) def test_correct(self, isolation): classifiers = [ - 'Programming Language :: Python :: 3.11', - 'Programming Language :: Python :: 3.11', - 'Programming Language :: Python :: 3.9', - 'Development Status :: 4 - Beta', - 'Private :: Do Not Upload', + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.9", + "Development Status :: 4 - Beta", + "Private :: Do Not Upload", ] - metadata = ProjectMetadata(str(isolation), None, {'project': {'classifiers': classifiers}}) + metadata = ProjectMetadata(str(isolation), None, {"project": {"classifiers": classifiers}}) assert ( metadata.core.classifiers == metadata.core.classifiers == [ - 'Private :: Do Not Upload', - 'Development Status :: 4 - Beta', - 'Programming Language :: Python :: 3.9', - 'Programming Language :: Python :: 3.11', + "Private :: Do Not Upload", + "Development Status :: 4 - Beta", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.11", ] ) class TestURLs: def test_dynamic(self, isolation): - metadata = ProjectMetadata(str(isolation), None, {'project': {'urls': 9000, 'dynamic': ['urls']}}) + metadata = ProjectMetadata(str(isolation), None, {"project": {"urls": 9000, "dynamic": ["urls"]}}) with pytest.raises( ValueError, - match='Metadata field `urls` cannot be both statically defined and listed in field `project.dynamic`', + match="Metadata field `urls` cannot be both statically defined and listed in field `project.dynamic`", ): _ = metadata.core.urls def test_not_table(self, isolation): - metadata = ProjectMetadata(str(isolation), None, {'project': {'urls': 10}}) + metadata = ProjectMetadata(str(isolation), None, {"project": {"urls": 10}}) - with pytest.raises(TypeError, match='Field `project.urls` must be a table'): + with pytest.raises(TypeError, match="Field `project.urls` must be a table"): _ = metadata.core.urls def test_entry_not_string(self, isolation): - metadata = ProjectMetadata(str(isolation), None, {'project': {'urls': {'foo': 7}}}) + metadata = ProjectMetadata(str(isolation), None, {"project": {"urls": {"foo": 7}}}) - with pytest.raises(TypeError, match='URL `foo` of field `project.urls` must be a string'): + with pytest.raises(TypeError, match="URL `foo` of field `project.urls` must be a string"): _ = metadata.core.urls def test_correct(self, isolation): - metadata = ProjectMetadata(str(isolation), None, {'project': {'urls': {'foo': 'bar', 'bar': 'baz'}}}) + metadata = ProjectMetadata(str(isolation), None, {"project": {"urls": {"foo": "bar", "bar": "baz"}}}) - assert metadata.core.urls == metadata.core.urls == {'bar': 'baz', 'foo': 'bar'} + assert metadata.core.urls == metadata.core.urls == {"bar": "baz", "foo": "bar"} class TestScripts: def test_dynamic(self, isolation): - metadata = ProjectMetadata(str(isolation), None, {'project': {'scripts': 9000, 'dynamic': ['scripts']}}) + metadata = ProjectMetadata(str(isolation), None, {"project": {"scripts": 9000, "dynamic": ["scripts"]}}) with pytest.raises( ValueError, - match='Metadata field `scripts` cannot be both statically defined and listed in field `project.dynamic`', + match="Metadata field `scripts` cannot be both statically defined and listed in field `project.dynamic`", ): _ = metadata.core.scripts def test_not_table(self, isolation): - metadata = ProjectMetadata(str(isolation), None, {'project': {'scripts': 10}}) + metadata = ProjectMetadata(str(isolation), None, {"project": {"scripts": 10}}) - with pytest.raises(TypeError, match='Field `project.scripts` must be a table'): + with pytest.raises(TypeError, match="Field `project.scripts` must be a table"): _ = metadata.core.scripts def test_entry_not_string(self, isolation): - metadata = ProjectMetadata(str(isolation), None, {'project': {'scripts': {'foo': 7}}}) + metadata = ProjectMetadata(str(isolation), None, {"project": {"scripts": {"foo": 7}}}) - with pytest.raises(TypeError, match='Object reference `foo` of field `project.scripts` must be a string'): + with pytest.raises(TypeError, match="Object reference `foo` of field `project.scripts` must be a string"): _ = metadata.core.scripts def test_correct(self, isolation): - metadata = ProjectMetadata(str(isolation), None, {'project': {'scripts': {'foo': 'bar', 'bar': 'baz'}}}) + metadata = ProjectMetadata(str(isolation), None, {"project": {"scripts": {"foo": "bar", "bar": "baz"}}}) - assert metadata.core.scripts == metadata.core.scripts == {'bar': 'baz', 'foo': 'bar'} + assert metadata.core.scripts == metadata.core.scripts == {"bar": "baz", "foo": "bar"} class TestGUIScripts: def test_dynamic(self, isolation): - metadata = ProjectMetadata(str(isolation), None, {'project': {'gui-scripts': 9000, 'dynamic': ['gui-scripts']}}) + metadata = ProjectMetadata(str(isolation), None, {"project": {"gui-scripts": 9000, "dynamic": ["gui-scripts"]}}) with pytest.raises( ValueError, match=( - 'Metadata field `gui-scripts` cannot be both statically defined and listed in field `project.dynamic`' + "Metadata field `gui-scripts` cannot be both statically defined and listed in field `project.dynamic`" ), ): _ = metadata.core.gui_scripts def test_not_table(self, isolation): - metadata = ProjectMetadata(str(isolation), None, {'project': {'gui-scripts': 10}}) + metadata = ProjectMetadata(str(isolation), None, {"project": {"gui-scripts": 10}}) - with pytest.raises(TypeError, match='Field `project.gui-scripts` must be a table'): + with pytest.raises(TypeError, match="Field `project.gui-scripts` must be a table"): _ = metadata.core.gui_scripts def test_entry_not_string(self, isolation): - metadata = ProjectMetadata(str(isolation), None, {'project': {'gui-scripts': {'foo': 7}}}) + metadata = ProjectMetadata(str(isolation), None, {"project": {"gui-scripts": {"foo": 7}}}) - with pytest.raises(TypeError, match='Object reference `foo` of field `project.gui-scripts` must be a string'): + with pytest.raises(TypeError, match="Object reference `foo` of field `project.gui-scripts` must be a string"): _ = metadata.core.gui_scripts def test_correct(self, isolation): - metadata = ProjectMetadata(str(isolation), None, {'project': {'gui-scripts': {'foo': 'bar', 'bar': 'baz'}}}) + metadata = ProjectMetadata(str(isolation), None, {"project": {"gui-scripts": {"foo": "bar", "bar": "baz"}}}) - assert metadata.core.gui_scripts == metadata.core.gui_scripts == {'bar': 'baz', 'foo': 'bar'} + assert metadata.core.gui_scripts == metadata.core.gui_scripts == {"bar": "baz", "foo": "bar"} class TestEntryPoints: def test_dynamic(self, isolation): metadata = ProjectMetadata( - str(isolation), None, {'project': {'entry-points': 9000, 'dynamic': ['entry-points']}} + str(isolation), None, {"project": {"entry-points": 9000, "dynamic": ["entry-points"]}} ) with pytest.raises( ValueError, match=( - 'Metadata field `entry-points` cannot be both statically defined and listed in field `project.dynamic`' + "Metadata field `entry-points` cannot be both statically defined and listed in field `project.dynamic`" ), ): _ = metadata.core.entry_points def test_not_table(self, isolation): - metadata = ProjectMetadata(str(isolation), None, {'project': {'entry-points': 10}}) + metadata = ProjectMetadata(str(isolation), None, {"project": {"entry-points": 10}}) - with pytest.raises(TypeError, match='Field `project.entry-points` must be a table'): + with pytest.raises(TypeError, match="Field `project.entry-points` must be a table"): _ = metadata.core.entry_points - @pytest.mark.parametrize(('field', 'expected'), [('console_scripts', 'scripts'), ('gui-scripts', 'gui-scripts')]) + @pytest.mark.parametrize(("field", "expected"), [("console_scripts", "scripts"), ("gui-scripts", "gui-scripts")]) def test_forbidden_fields(self, isolation, field, expected): - metadata = ProjectMetadata(str(isolation), None, {'project': {'entry-points': {field: 'foo'}}}) + metadata = ProjectMetadata(str(isolation), None, {"project": {"entry-points": {field: "foo"}}}) with pytest.raises( ValueError, match=( - f'Field `{field}` must be defined as `project.{expected}` instead of ' - f'in the `project.entry-points` table' + f"Field `{field}` must be defined as `project.{expected}` instead of " + f"in the `project.entry-points` table" ), ): _ = metadata.core.entry_points def test_data_not_table(self, isolation): - metadata = ProjectMetadata(str(isolation), None, {'project': {'entry-points': {'foo': 7}}}) + metadata = ProjectMetadata(str(isolation), None, {"project": {"entry-points": {"foo": 7}}}) - with pytest.raises(TypeError, match='Field `project.entry-points.foo` must be a table'): + with pytest.raises(TypeError, match="Field `project.entry-points.foo` must be a table"): _ = metadata.core.entry_points def test_data_entry_not_string(self, isolation): - metadata = ProjectMetadata(str(isolation), None, {'project': {'entry-points': {'foo': {'bar': 4}}}}) + metadata = ProjectMetadata(str(isolation), None, {"project": {"entry-points": {"foo": {"bar": 4}}}}) with pytest.raises( - TypeError, match='Object reference `bar` of field `project.entry-points.foo` must be a string' + TypeError, match="Object reference `bar` of field `project.entry-points.foo` must be a string" ): _ = metadata.core.entry_points def test_data_empty(self, isolation): - metadata = ProjectMetadata(str(isolation), None, {'project': {'entry-points': {'foo': {}}}}) + metadata = ProjectMetadata(str(isolation), None, {"project": {"entry-points": {"foo": {}}}}) assert metadata.core.entry_points == metadata.core.entry_points == {} def test_default(self, isolation): - metadata = ProjectMetadata(str(isolation), None, {'project': {}}) + metadata = ProjectMetadata(str(isolation), None, {"project": {}}) assert metadata.core.entry_points == metadata.core.entry_points == {} @@ -1098,58 +1097,58 @@ def test_correct(self, isolation): metadata = ProjectMetadata( str(isolation), None, - {'project': {'entry-points': {'foo': {'bar': 'baz', 'foo': 'baz'}, 'bar': {'foo': 'baz', 'bar': 'baz'}}}}, + {"project": {"entry-points": {"foo": {"bar": "baz", "foo": "baz"}, "bar": {"foo": "baz", "bar": "baz"}}}}, ) assert ( metadata.core.entry_points == metadata.core.entry_points - == {'bar': {'bar': 'baz', 'foo': 'baz'}, 'foo': {'bar': 'baz', 'foo': 'baz'}} + == {"bar": {"bar": "baz", "foo": "baz"}, "foo": {"bar": "baz", "foo": "baz"}} ) class TestDependencies: def test_dynamic(self, isolation): metadata = ProjectMetadata( - str(isolation), None, {'project': {'dependencies': 9000, 'dynamic': ['dependencies']}} + str(isolation), None, {"project": {"dependencies": 9000, "dynamic": ["dependencies"]}} ) with pytest.raises( ValueError, match=( - 'Metadata field `dependencies` cannot be both statically defined and listed in field `project.dynamic`' + "Metadata field `dependencies` cannot be both statically defined and listed in field `project.dynamic`" ), ): _ = metadata.core.dependencies def test_not_array(self, isolation): - metadata = ProjectMetadata(str(isolation), None, {'project': {'dependencies': 10}}) + metadata = ProjectMetadata(str(isolation), None, {"project": {"dependencies": 10}}) - with pytest.raises(TypeError, match='Field `project.dependencies` must be an array'): + with pytest.raises(TypeError, match="Field `project.dependencies` must be an array"): _ = metadata.core.dependencies def test_entry_not_string(self, isolation): - metadata = ProjectMetadata(str(isolation), None, {'project': {'dependencies': [10]}}) + metadata = ProjectMetadata(str(isolation), None, {"project": {"dependencies": [10]}}) - with pytest.raises(TypeError, match='Dependency #1 of field `project.dependencies` must be a string'): + with pytest.raises(TypeError, match="Dependency #1 of field `project.dependencies` must be a string"): _ = metadata.core.dependencies def test_invalid(self, isolation): - metadata = ProjectMetadata(str(isolation), None, {'project': {'dependencies': ['foo^1']}}) + metadata = ProjectMetadata(str(isolation), None, {"project": {"dependencies": ["foo^1"]}}) - with pytest.raises(ValueError, match='Dependency #1 of field `project.dependencies` is invalid: .+'): + with pytest.raises(ValueError, match="Dependency #1 of field `project.dependencies` is invalid: .+"): _ = metadata.core.dependencies def test_direct_reference(self, isolation): metadata = ProjectMetadata( - str(isolation), None, {'project': {'dependencies': ['proj @ git+https://github.com/org/proj.git@v1']}} + str(isolation), None, {"project": {"dependencies": ["proj @ git+https://github.com/org/proj.git@v1"]}} ) with pytest.raises( ValueError, match=( - 'Dependency #1 of field `project.dependencies` cannot be a direct reference unless ' - 'field `tool.hatch.metadata.allow-direct-references` is set to `true`' + "Dependency #1 of field `project.dependencies` cannot be a direct reference unless " + "field `tool.hatch.metadata.allow-direct-references` is set to `true`" ), ): _ = metadata.core.dependencies @@ -1159,35 +1158,35 @@ def test_direct_reference_allowed(self, isolation): str(isolation), None, { - 'project': {'dependencies': ['proj @ git+https://github.com/org/proj.git@v1']}, - 'tool': {'hatch': {'metadata': {'allow-direct-references': True}}}, + "project": {"dependencies": ["proj @ git+https://github.com/org/proj.git@v1"]}, + "tool": {"hatch": {"metadata": {"allow-direct-references": True}}}, }, ) - assert metadata.core.dependencies == ['proj@ git+https://github.com/org/proj.git@v1'] + assert metadata.core.dependencies == ["proj@ git+https://github.com/org/proj.git@v1"] def test_context_formatting(self, isolation, uri_slash_prefix): metadata = ProjectMetadata( str(isolation), None, { - 'project': {'dependencies': ['proj @ {root:uri}']}, - 'tool': {'hatch': {'metadata': {'allow-direct-references': True}}}, + "project": {"dependencies": ["proj @ {root:uri}"]}, + "tool": {"hatch": {"metadata": {"allow-direct-references": True}}}, }, ) - normalized_path = str(isolation).replace('\\', '/') - assert metadata.core.dependencies == [f'proj@ file:{uri_slash_prefix}{normalized_path}'] + normalized_path = str(isolation).replace("\\", "/") + assert metadata.core.dependencies == [f"proj@ file:{uri_slash_prefix}{normalized_path}"] def test_correct(self, isolation): metadata = ProjectMetadata( str(isolation), None, { - 'project': { - 'dependencies': [ + "project": { + "dependencies": [ 'python___dateutil;platform_python_implementation=="CPython"', - 'bAr.Baz[TLS, Zu.Bat, EdDSA, Zu_Bat] >=1.2RC5 , <9000B1', + "bAr.Baz[TLS, Zu.Bat, EdDSA, Zu_Bat] >=1.2RC5 , <9000B1", 'Foo;python_version<"3.8"', 'fOO; python_version< "3.8"', ], @@ -1199,7 +1198,7 @@ def test_correct(self, isolation): metadata.core.dependencies == metadata.core.dependencies == [ - 'bar-baz[eddsa,tls,zu-bat]<9000b1,>=1.2rc5', + "bar-baz[eddsa,tls,zu-bat]<9000b1,>=1.2rc5", "foo; python_version < '3.8'", "python-dateutil; platform_python_implementation == 'CPython'", ] @@ -1210,71 +1209,71 @@ def test_correct(self, isolation): class TestOptionalDependencies: def test_dynamic(self, isolation): metadata = ProjectMetadata( - str(isolation), None, {'project': {'optional-dependencies': 9000, 'dynamic': ['optional-dependencies']}} + str(isolation), None, {"project": {"optional-dependencies": 9000, "dynamic": ["optional-dependencies"]}} ) with pytest.raises( ValueError, match=( - 'Metadata field `optional-dependencies` cannot be both statically defined and ' - 'listed in field `project.dynamic`' + "Metadata field `optional-dependencies` cannot be both statically defined and " + "listed in field `project.dynamic`" ), ): _ = metadata.core.optional_dependencies def test_not_table(self, isolation): - metadata = ProjectMetadata(str(isolation), None, {'project': {'optional-dependencies': 10}}) + metadata = ProjectMetadata(str(isolation), None, {"project": {"optional-dependencies": 10}}) - with pytest.raises(TypeError, match='Field `project.optional-dependencies` must be a table'): + with pytest.raises(TypeError, match="Field `project.optional-dependencies` must be a table"): _ = metadata.core.optional_dependencies def test_invalid_name(self, isolation): - metadata = ProjectMetadata(str(isolation), None, {'project': {'optional-dependencies': {'foo/bar': []}}}) + metadata = ProjectMetadata(str(isolation), None, {"project": {"optional-dependencies": {"foo/bar": []}}}) with pytest.raises( ValueError, match=( - 'Optional dependency group `foo/bar` of field `project.optional-dependencies` must only contain ' - 'ASCII letters/digits, underscores, hyphens, and periods, and must begin and end with ' - 'ASCII letters/digits.' + "Optional dependency group `foo/bar` of field `project.optional-dependencies` must only contain " + "ASCII letters/digits, underscores, hyphens, and periods, and must begin and end with " + "ASCII letters/digits." ), ): _ = metadata.core.optional_dependencies def test_definitions_not_array(self, isolation): - metadata = ProjectMetadata(str(isolation), None, {'project': {'optional-dependencies': {'foo': 5}}}) + metadata = ProjectMetadata(str(isolation), None, {"project": {"optional-dependencies": {"foo": 5}}}) with pytest.raises( - TypeError, match='Dependencies for option `foo` of field `project.optional-dependencies` must be an array' + TypeError, match="Dependencies for option `foo` of field `project.optional-dependencies` must be an array" ): _ = metadata.core.optional_dependencies def test_entry_not_string(self, isolation): - metadata = ProjectMetadata(str(isolation), None, {'project': {'optional-dependencies': {'foo': [5]}}}) + metadata = ProjectMetadata(str(isolation), None, {"project": {"optional-dependencies": {"foo": [5]}}}) with pytest.raises( - TypeError, match='Dependency #1 of option `foo` of field `project.optional-dependencies` must be a string' + TypeError, match="Dependency #1 of option `foo` of field `project.optional-dependencies` must be a string" ): _ = metadata.core.optional_dependencies def test_invalid(self, isolation): - metadata = ProjectMetadata(str(isolation), None, {'project': {'optional-dependencies': {'foo': ['bar^1']}}}) + metadata = ProjectMetadata(str(isolation), None, {"project": {"optional-dependencies": {"foo": ["bar^1"]}}}) with pytest.raises( - ValueError, match='Dependency #1 of option `foo` of field `project.optional-dependencies` is invalid: .+' + ValueError, match="Dependency #1 of option `foo` of field `project.optional-dependencies` is invalid: .+" ): _ = metadata.core.optional_dependencies def test_conflict(self, isolation): metadata = ProjectMetadata( - str(isolation), None, {'project': {'optional-dependencies': {'foo_bar': [], 'foo.bar': []}}} + str(isolation), None, {"project": {"optional-dependencies": {"foo_bar": [], "foo.bar": []}}} ) with pytest.raises( ValueError, match=( - 'Optional dependency groups `foo_bar` and `foo.bar` of field `project.optional-dependencies` both ' - 'evaluate to `foo-bar`.' + "Optional dependency groups `foo_bar` and `foo.bar` of field `project.optional-dependencies` both " + "evaluate to `foo-bar`." ), ): _ = metadata.core.optional_dependencies @@ -1283,12 +1282,12 @@ def test_recursive_circular(self, isolation): metadata = ProjectMetadata( str(isolation), None, - {'project': {'name': 'my-app', 'optional-dependencies': {'foo': ['my-app[bar]'], 'bar': ['my-app[foo]']}}}, + {"project": {"name": "my-app", "optional-dependencies": {"foo": ["my-app[bar]"], "bar": ["my-app[foo]"]}}}, ) with pytest.raises( ValueError, - match='Field `project.optional-dependencies` defines a circular dependency group: foo', + match="Field `project.optional-dependencies` defines a circular dependency group: foo", ): _ = metadata.core.optional_dependencies @@ -1296,12 +1295,12 @@ def test_recursive_unknown(self, isolation): metadata = ProjectMetadata( str(isolation), None, - {'project': {'name': 'my-app', 'optional-dependencies': {'foo': ['my-app[bar]']}}}, + {"project": {"name": "my-app", "optional-dependencies": {"foo": ["my-app[bar]"]}}}, ) with pytest.raises( ValueError, - match='Unknown recursive dependency group in field `project.optional-dependencies`: bar', + match="Unknown recursive dependency group in field `project.optional-dependencies`: bar", ): _ = metadata.core.optional_dependencies @@ -1310,25 +1309,25 @@ def test_allow_ambiguity(self, isolation): str(isolation), None, { - 'project': {'optional-dependencies': {'foo_bar': [], 'foo.bar': []}}, - 'tool': {'hatch': {'metadata': {'allow-ambiguous-features': True}}}, + "project": {"optional-dependencies": {"foo_bar": [], "foo.bar": []}}, + "tool": {"hatch": {"metadata": {"allow-ambiguous-features": True}}}, }, ) - assert metadata.core.optional_dependencies == {'foo_bar': [], 'foo.bar': []} + assert metadata.core.optional_dependencies == {"foo_bar": [], "foo.bar": []} def test_direct_reference(self, isolation): metadata = ProjectMetadata( str(isolation), None, - {'project': {'optional-dependencies': {'foo': ['proj @ git+https://github.com/org/proj.git@v1']}}}, + {"project": {"optional-dependencies": {"foo": ["proj @ git+https://github.com/org/proj.git@v1"]}}}, ) with pytest.raises( ValueError, match=( - 'Dependency #1 of option `foo` of field `project.optional-dependencies` cannot be a direct reference ' - 'unless field `tool.hatch.metadata.allow-direct-references` is set to `true`' + "Dependency #1 of option `foo` of field `project.optional-dependencies` cannot be a direct reference " + "unless field `tool.hatch.metadata.allow-direct-references` is set to `true`" ), ): _ = metadata.core.optional_dependencies @@ -1338,48 +1337,48 @@ def test_context_formatting(self, isolation, uri_slash_prefix): str(isolation), None, { - 'project': {'name': 'my-app', 'optional-dependencies': {'foo': ['proj @ {root:uri}']}}, - 'tool': {'hatch': {'metadata': {'allow-direct-references': True}}}, + "project": {"name": "my-app", "optional-dependencies": {"foo": ["proj @ {root:uri}"]}}, + "tool": {"hatch": {"metadata": {"allow-direct-references": True}}}, }, ) - normalized_path = str(isolation).replace('\\', '/') - assert metadata.core.optional_dependencies == {'foo': [f'proj@ file:{uri_slash_prefix}{normalized_path}']} + normalized_path = str(isolation).replace("\\", "/") + assert metadata.core.optional_dependencies == {"foo": [f"proj@ file:{uri_slash_prefix}{normalized_path}"]} def test_direct_reference_allowed(self, isolation): metadata = ProjectMetadata( str(isolation), None, { - 'project': { - 'name': 'my-app', - 'optional-dependencies': {'foo': ['proj @ git+https://github.com/org/proj.git@v1']}, + "project": { + "name": "my-app", + "optional-dependencies": {"foo": ["proj @ git+https://github.com/org/proj.git@v1"]}, }, - 'tool': {'hatch': {'metadata': {'allow-direct-references': True}}}, + "tool": {"hatch": {"metadata": {"allow-direct-references": True}}}, }, ) - assert metadata.core.optional_dependencies == {'foo': ['proj@ git+https://github.com/org/proj.git@v1']} + assert metadata.core.optional_dependencies == {"foo": ["proj@ git+https://github.com/org/proj.git@v1"]} def test_correct(self, isolation): metadata = ProjectMetadata( str(isolation), None, { - 'project': { - 'name': 'my-app', - 'optional-dependencies': { - 'foo': [ + "project": { + "name": "my-app", + "optional-dependencies": { + "foo": [ 'python___dateutil;platform_python_implementation=="CPython"', - 'bAr.Baz[TLS, Zu.Bat, EdDSA, Zu_Bat] >=1.2RC5 , <9000B1', + "bAr.Baz[TLS, Zu.Bat, EdDSA, Zu_Bat] >=1.2RC5 , <9000B1", 'Foo;python_version<"3.8"', 'fOO; python_version< "3.8"', - 'MY-APP[zZz]', + "MY-APP[zZz]", ], - 'bar': ['foo', 'bar', 'Baz'], - 'baz': ['my___app[XYZ]'], - 'xyz': ['my...app[Bar]'], - 'zzz': ['aaa'], + "bar": ["foo", "bar", "Baz"], + "baz": ["my___app[XYZ]"], + "xyz": ["my...app[Bar]"], + "zzz": ["aaa"], }, }, }, @@ -1389,16 +1388,16 @@ def test_correct(self, isolation): metadata.core.optional_dependencies == metadata.core.optional_dependencies == { - 'bar': ['bar', 'baz', 'foo'], - 'baz': ['bar', 'baz', 'foo'], - 'foo': [ - 'aaa', - 'bar-baz[eddsa,tls,zu-bat]<9000b1,>=1.2rc5', + "bar": ["bar", "baz", "foo"], + "baz": ["bar", "baz", "foo"], + "foo": [ + "aaa", + "bar-baz[eddsa,tls,zu-bat]<9000b1,>=1.2rc5", "foo; python_version < '3.8'", "python-dateutil; platform_python_implementation == 'CPython'", ], - 'xyz': ['bar', 'baz', 'foo'], - 'zzz': ['aaa'], + "xyz": ["bar", "baz", "foo"], + "zzz": ["aaa"], } ) @@ -1408,31 +1407,31 @@ def test_unknown(self, isolation): metadata = ProjectMetadata( str(isolation), PluginManager(), - {'project': {'name': 'foo'}, 'tool': {'hatch': {'metadata': {'hooks': {'foo': {}}}}}}, + {"project": {"name": "foo"}, "tool": {"hatch": {"metadata": {"hooks": {"foo": {}}}}}}, ) - with pytest.raises(ValueError, match='Unknown metadata hook: foo'): + with pytest.raises(ValueError, match="Unknown metadata hook: foo"): _ = metadata.core def test_custom(self, temp_dir, helpers): classifiers = [ - 'Programming Language :: Python :: 3.11', - 'Programming Language :: Python :: 3.11', - 'Programming Language :: Python :: 3.9', - 'Framework :: Foo', - 'Development Status :: 4 - Beta', - 'Private :: Do Not Upload', + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.9", + "Framework :: Foo", + "Development Status :: 4 - Beta", + "Private :: Do Not Upload", ] metadata = ProjectMetadata( str(temp_dir), PluginManager(), { - 'project': {'name': 'foo', 'classifiers': classifiers, 'dynamic': ['version', 'description']}, - 'tool': {'hatch': {'version': {'path': 'a/b'}, 'metadata': {'hooks': {'custom': {}}}}}, + "project": {"name": "foo", "classifiers": classifiers, "dynamic": ["version", "description"]}, + "tool": {"hatch": {"version": {"path": "a/b"}, "metadata": {"hooks": {"custom": {}}}}}, }, ) - file_path = temp_dir / 'a' / 'b' + file_path = temp_dir / "a" / "b" file_path.ensure_parent_dir_exists() file_path.write_text('__version__ = "0.0.1"') @@ -1453,16 +1452,16 @@ def get_known_classifiers(self): ) ) - assert 'custom' in metadata.hatch.metadata.hooks - assert metadata.core.name == 'foo' - assert metadata.core.description == 'foobar' - assert metadata.core.version == '0.0.1rc0' + assert "custom" in metadata.hatch.metadata.hooks + assert metadata.core.name == "foo" + assert metadata.core.description == "foobar" + assert metadata.core.version == "0.0.1rc0" assert metadata.core.classifiers == [ - 'Private :: Do Not Upload', - 'Development Status :: 4 - Beta', - 'Framework :: Foo', - 'Programming Language :: Python :: 3.9', - 'Programming Language :: Python :: 3.11', + "Private :: Do Not Upload", + "Development Status :: 4 - Beta", + "Framework :: Foo", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.11", ] def test_custom_missing_dynamic(self, temp_dir, helpers): @@ -1470,12 +1469,12 @@ def test_custom_missing_dynamic(self, temp_dir, helpers): str(temp_dir), PluginManager(), { - 'project': {'name': 'foo', 'dynamic': ['version']}, - 'tool': {'hatch': {'version': {'path': 'a/b'}, 'metadata': {'hooks': {'custom': {}}}}}, + "project": {"name": "foo", "dynamic": ["version"]}, + "tool": {"hatch": {"version": {"path": "a/b"}, "metadata": {"hooks": {"custom": {}}}}}, }, ) - file_path = temp_dir / 'a' / 'b' + file_path = temp_dir / "a" / "b" file_path.ensure_parent_dir_exists() file_path.write_text('__version__ = "0.0.1"') @@ -1494,7 +1493,7 @@ def update(self, metadata): with pytest.raises( ValueError, - match='The field `description` was set dynamically and therefore must be listed in `project.dynamic`', + match="The field `description` was set dynamically and therefore must be listed in `project.dynamic`", ): _ = metadata.core @@ -1505,17 +1504,17 @@ def test_correct(self, temp_dir, helpers): str(temp_dir), PluginManager(), { - 'project': {'name': 'foo', 'dynamic': ['version']}, - 'tool': {'hatch': {'build': {'reproducible': False}}}, + "project": {"name": "foo", "dynamic": ["version"]}, + "tool": {"hatch": {"build": {"reproducible": False}}}, }, ) - file_path = temp_dir / 'a' / 'b' + file_path = temp_dir / "a" / "b" file_path.ensure_parent_dir_exists() file_path.write_text('__version__ = "0.0.1"') - (temp_dir / 'pyproject.toml').touch() - file_path = temp_dir / 'hatch.toml' + (temp_dir / "pyproject.toml").touch() + file_path = temp_dir / "hatch.toml" file_path.write_text( helpers.dedent( """ @@ -1525,29 +1524,29 @@ def test_correct(self, temp_dir, helpers): ) ) - assert metadata.version == '0.0.1' - assert metadata.hatch.build_config['reproducible'] is False + assert metadata.version == "0.0.1" + assert metadata.hatch.build_config["reproducible"] is False def test_precedence(self, temp_dir, helpers): metadata = ProjectMetadata( str(temp_dir), PluginManager(), { - 'project': {'name': 'foo', 'dynamic': ['version']}, - 'tool': {'hatch': {'version': {'path': 'a/b'}, 'build': {'reproducible': False}}}, + "project": {"name": "foo", "dynamic": ["version"]}, + "tool": {"hatch": {"version": {"path": "a/b"}, "build": {"reproducible": False}}}, }, ) - file_path = temp_dir / 'a' / 'b' + file_path = temp_dir / "a" / "b" file_path.ensure_parent_dir_exists() file_path.write_text('__version__ = "0.0.1"') - file_path = temp_dir / 'c' / 'd' + file_path = temp_dir / "c" / "d" file_path.ensure_parent_dir_exists() file_path.write_text('__version__ = "0.0.2"') - (temp_dir / 'pyproject.toml').touch() - file_path = temp_dir / 'hatch.toml' + (temp_dir / "pyproject.toml").touch() + file_path = temp_dir / "hatch.toml" file_path.write_text( helpers.dedent( """ @@ -1557,138 +1556,138 @@ def test_precedence(self, temp_dir, helpers): ) ) - assert metadata.version == '0.0.2' - assert metadata.hatch.build_config['reproducible'] is False + assert metadata.version == "0.0.2" + assert metadata.hatch.build_config["reproducible"] is False class TestMetadataConversion: def test_required_only(self, isolation, latest_spec): - raw_metadata = {'name': 'My.App', 'version': '0.0.1'} - metadata = ProjectMetadata(str(isolation), None, {'project': raw_metadata}) + raw_metadata = {"name": "My.App", "version": "0.0.1"} + metadata = ProjectMetadata(str(isolation), None, {"project": raw_metadata}) core_metadata = latest_spec(metadata) assert project_metadata_from_core_metadata(core_metadata) == raw_metadata def test_dynamic(self, isolation, latest_spec): - raw_metadata = {'name': 'My.App', 'version': '0.0.1', 'dynamic': ['authors', 'classifiers']} - metadata = ProjectMetadata(str(isolation), None, {'project': raw_metadata}) + raw_metadata = {"name": "My.App", "version": "0.0.1", "dynamic": ["authors", "classifiers"]} + metadata = ProjectMetadata(str(isolation), None, {"project": raw_metadata}) core_metadata = latest_spec(metadata) assert project_metadata_from_core_metadata(core_metadata) == raw_metadata def test_description(self, isolation, latest_spec): - raw_metadata = {'name': 'My.App', 'version': '0.0.1', 'description': 'foo bar'} - metadata = ProjectMetadata(str(isolation), None, {'project': raw_metadata}) + raw_metadata = {"name": "My.App", "version": "0.0.1", "description": "foo bar"} + metadata = ProjectMetadata(str(isolation), None, {"project": raw_metadata}) core_metadata = latest_spec(metadata) assert project_metadata_from_core_metadata(core_metadata) == raw_metadata def test_urls(self, isolation, latest_spec): - raw_metadata = {'name': 'My.App', 'version': '0.0.1', 'urls': {'foo': 'bar', 'bar': 'baz'}} - metadata = ProjectMetadata(str(isolation), None, {'project': raw_metadata}) + raw_metadata = {"name": "My.App", "version": "0.0.1", "urls": {"foo": "bar", "bar": "baz"}} + metadata = ProjectMetadata(str(isolation), None, {"project": raw_metadata}) core_metadata = latest_spec(metadata) assert project_metadata_from_core_metadata(core_metadata) == raw_metadata def test_authors(self, isolation, latest_spec): raw_metadata = { - 'name': 'My.App', - 'version': '0.0.1', - 'authors': [{'name': 'foobar'}, {'email': 'bar@domain', 'name': 'foo'}, {'email': 'baz@domain'}], + "name": "My.App", + "version": "0.0.1", + "authors": [{"name": "foobar"}, {"email": "bar@domain", "name": "foo"}, {"email": "baz@domain"}], } - metadata = ProjectMetadata(str(isolation), None, {'project': raw_metadata}) + metadata = ProjectMetadata(str(isolation), None, {"project": raw_metadata}) core_metadata = latest_spec(metadata) assert project_metadata_from_core_metadata(core_metadata) == raw_metadata def test_maintainers(self, isolation, latest_spec): raw_metadata = { - 'name': 'My.App', - 'version': '0.0.1', - 'maintainers': [{'name': 'foobar'}, {'email': 'bar@domain', 'name': 'foo'}, {'email': 'baz@domain'}], + "name": "My.App", + "version": "0.0.1", + "maintainers": [{"name": "foobar"}, {"email": "bar@domain", "name": "foo"}, {"email": "baz@domain"}], } - metadata = ProjectMetadata(str(isolation), None, {'project': raw_metadata}) + metadata = ProjectMetadata(str(isolation), None, {"project": raw_metadata}) core_metadata = latest_spec(metadata) assert project_metadata_from_core_metadata(core_metadata) == raw_metadata def test_keywords(self, isolation, latest_spec): - raw_metadata = {'name': 'My.App', 'version': '0.0.1', 'keywords': ['bar', 'foo']} - metadata = ProjectMetadata(str(isolation), None, {'project': raw_metadata}) + raw_metadata = {"name": "My.App", "version": "0.0.1", "keywords": ["bar", "foo"]} + metadata = ProjectMetadata(str(isolation), None, {"project": raw_metadata}) core_metadata = latest_spec(metadata) assert project_metadata_from_core_metadata(core_metadata) == raw_metadata def test_classifiers(self, isolation, latest_spec): raw_metadata = { - 'name': 'My.App', - 'version': '0.0.1', - 'classifiers': ['Programming Language :: Python :: 3.9', 'Programming Language :: Python :: 3.11'], + "name": "My.App", + "version": "0.0.1", + "classifiers": ["Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.11"], } - metadata = ProjectMetadata(str(isolation), None, {'project': raw_metadata}) + metadata = ProjectMetadata(str(isolation), None, {"project": raw_metadata}) core_metadata = latest_spec(metadata) assert project_metadata_from_core_metadata(core_metadata) == raw_metadata def test_license_files(self, temp_dir, latest_spec): raw_metadata = { - 'name': 'My.App', - 'version': '0.0.1', - 'license-files': ['LICENSES/Apache-2.0.txt', 'LICENSES/MIT.txt'], + "name": "My.App", + "version": "0.0.1", + "license-files": ["LICENSES/Apache-2.0.txt", "LICENSES/MIT.txt"], } - metadata = ProjectMetadata(str(temp_dir), None, {'project': raw_metadata}) + metadata = ProjectMetadata(str(temp_dir), None, {"project": raw_metadata}) - licenses_path = temp_dir / 'LICENSES' + licenses_path = temp_dir / "LICENSES" licenses_path.mkdir() - licenses_path.joinpath('Apache-2.0.txt').touch() - licenses_path.joinpath('MIT.txt').touch() + licenses_path.joinpath("Apache-2.0.txt").touch() + licenses_path.joinpath("MIT.txt").touch() core_metadata = latest_spec(metadata) assert project_metadata_from_core_metadata(core_metadata) == raw_metadata def test_license_expression(self, isolation, latest_spec): - raw_metadata = {'name': 'My.App', 'version': '0.0.1', 'license': 'MIT'} - metadata = ProjectMetadata(str(isolation), None, {'project': raw_metadata}) + raw_metadata = {"name": "My.App", "version": "0.0.1", "license": "MIT"} + metadata = ProjectMetadata(str(isolation), None, {"project": raw_metadata}) core_metadata = latest_spec(metadata) assert project_metadata_from_core_metadata(core_metadata) == raw_metadata def test_license_legacy(self, isolation, latest_spec): - raw_metadata = {'name': 'My.App', 'version': '0.0.1', 'license': {'text': 'foo'}} - metadata = ProjectMetadata(str(isolation), None, {'project': raw_metadata}) + raw_metadata = {"name": "My.App", "version": "0.0.1", "license": {"text": "foo"}} + metadata = ProjectMetadata(str(isolation), None, {"project": raw_metadata}) core_metadata = latest_spec(metadata) assert project_metadata_from_core_metadata(core_metadata) == raw_metadata def test_readme(self, isolation, latest_spec): raw_metadata = { - 'name': 'My.App', - 'version': '0.0.1', - 'readme': {'content-type': 'text/markdown', 'text': 'test content\n'}, + "name": "My.App", + "version": "0.0.1", + "readme": {"content-type": "text/markdown", "text": "test content\n"}, } - metadata = ProjectMetadata(str(isolation), None, {'project': raw_metadata}) + metadata = ProjectMetadata(str(isolation), None, {"project": raw_metadata}) core_metadata = latest_spec(metadata) assert project_metadata_from_core_metadata(core_metadata) == raw_metadata def test_requires_python(self, isolation, latest_spec): - raw_metadata = {'name': 'My.App', 'version': '0.0.1', 'requires-python': '<2,>=1'} - metadata = ProjectMetadata(str(isolation), None, {'project': raw_metadata}) + raw_metadata = {"name": "My.App", "version": "0.0.1", "requires-python": "<2,>=1"} + metadata = ProjectMetadata(str(isolation), None, {"project": raw_metadata}) core_metadata = latest_spec(metadata) assert project_metadata_from_core_metadata(core_metadata) == raw_metadata def test_dependencies(self, isolation, latest_spec): raw_metadata = { - 'name': 'My.App', - 'version': '0.0.1', - 'dependencies': ['bar==5', 'foo==1'], - 'optional-dependencies': { - 'feature1': ['bar==5; python_version < "3"', 'foo==1'], - 'feature2': ['bar==5', 'foo==1; python_version < "3"'], + "name": "My.App", + "version": "0.0.1", + "dependencies": ["bar==5", "foo==1"], + "optional-dependencies": { + "feature1": ['bar==5; python_version < "3"', "foo==1"], + "feature2": ["bar==5", 'foo==1; python_version < "3"'], }, } - metadata = ProjectMetadata(str(isolation), None, {'project': raw_metadata}) + metadata = ProjectMetadata(str(isolation), None, {"project": raw_metadata}) core_metadata = latest_spec(metadata) assert project_metadata_from_core_metadata(core_metadata) == raw_metadata @@ -1700,16 +1699,16 @@ def test_basic_persistence(self, temp_dir, helpers): str(temp_dir), None, { - 'project': { - 'name': 'My.App', - 'dynamic': ['version', 'keywords'], - 'dependencies': ['foo==1'], - 'scripts': {'foo': 'bar'}, + "project": { + "name": "My.App", + "dynamic": ["version", "keywords"], + "dependencies": ["foo==1"], + "scripts": {"foo": "bar"}, }, }, ) - pkg_info = temp_dir / 'PKG-INFO' + pkg_info = temp_dir / "PKG-INFO" pkg_info.write_text( helpers.dedent( f""" @@ -1723,12 +1722,12 @@ def test_basic_persistence(self, temp_dir, helpers): ) with temp_dir.as_cwd(): assert metadata.core.config == { - 'name': 'My.App', - 'version': '0.0.1', - 'dependencies': ['foo==1'], - 'keywords': ['foo', 'bar'], - 'scripts': {'foo': 'bar'}, - 'dynamic': [], + "name": "My.App", + "version": "0.0.1", + "dependencies": ["foo==1"], + "keywords": ["foo", "bar"], + "scripts": {"foo": "bar"}, + "dynamic": [], } def test_metadata_hooks(self, temp_dir, helpers): @@ -1736,12 +1735,12 @@ def test_metadata_hooks(self, temp_dir, helpers): str(temp_dir), PluginManager(), { - 'project': { - 'name': 'My.App', - 'dynamic': ['version', 'keywords', 'description', 'scripts'], - 'dependencies': ['foo==1'], + "project": { + "name": "My.App", + "dynamic": ["version", "keywords", "description", "scripts"], + "dependencies": ["foo==1"], }, - 'tool': {'hatch': {'metadata': {'hooks': {'custom': {}}}}}, + "tool": {"hatch": {"metadata": {"hooks": {"custom": {}}}}}, }, ) @@ -1759,7 +1758,7 @@ def update(self, metadata): ) ) - pkg_info = temp_dir / 'PKG-INFO' + pkg_info = temp_dir / "PKG-INFO" pkg_info.write_text( helpers.dedent( f""" @@ -1774,11 +1773,11 @@ def update(self, metadata): ) with temp_dir.as_cwd(): assert metadata.core.config == { - 'name': 'My.App', - 'version': '0.0.1', - 'description': 'My.App bar', - 'dependencies': ['foo==1'], - 'keywords': ['foo', 'bar'], - 'scripts': {'foo': 'bar'}, - 'dynamic': ['scripts'], + "name": "My.App", + "version": "0.0.1", + "description": "My.App bar", + "dependencies": ["foo==1"], + "keywords": ["foo", "bar"], + "scripts": {"foo": "bar"}, + "dynamic": ["scripts"], } diff --git a/tests/backend/metadata/test_custom_hook.py b/tests/backend/metadata/test_custom_hook.py index ad83c0e7b..c5ceca223 100644 --- a/tests/backend/metadata/test_custom_hook.py +++ b/tests/backend/metadata/test_custom_hook.py @@ -7,23 +7,23 @@ def test_no_path(isolation): - config = {'path': ''} + config = {"path": ""} - with pytest.raises(ValueError, match='Option `path` for metadata hook `custom` must not be empty if defined'): + with pytest.raises(ValueError, match="Option `path` for metadata hook `custom` must not be empty if defined"): CustomMetadataHook(str(isolation), config) def test_path_not_string(isolation): - config = {'path': 3} + config = {"path": 3} - with pytest.raises(TypeError, match='Option `path` for metadata hook `custom` must be a string'): + with pytest.raises(TypeError, match="Option `path` for metadata hook `custom` must be a string"): CustomMetadataHook(str(isolation), config) def test_nonexistent(isolation): - config = {'path': 'test.py'} + config = {"path": "test.py"} - with pytest.raises(OSError, match='Build script does not exist: test.py'): + with pytest.raises(OSError, match="Build script does not exist: test.py"): CustomMetadataHook(str(isolation), config) @@ -49,13 +49,13 @@ def foo(self): with temp_dir.as_cwd(): hook = CustomMetadataHook(str(temp_dir), config) - assert hook.foo() == ('custom', str(temp_dir)) + assert hook.foo() == ("custom", str(temp_dir)) def test_explicit_path(temp_dir, helpers): - config = {'path': f'foo/{DEFAULT_BUILD_SCRIPT}'} + config = {"path": f"foo/{DEFAULT_BUILD_SCRIPT}"} - file_path = temp_dir / 'foo' / DEFAULT_BUILD_SCRIPT + file_path = temp_dir / "foo" / DEFAULT_BUILD_SCRIPT file_path.ensure_parent_dir_exists() file_path.write_text( helpers.dedent( @@ -75,13 +75,13 @@ def foo(self): with temp_dir.as_cwd(): hook = CustomMetadataHook(str(temp_dir), config) - assert hook.foo() == ('custom', str(temp_dir)) + assert hook.foo() == ("custom", str(temp_dir)) def test_no_subclass(temp_dir, helpers): - config = {'path': f'foo/{DEFAULT_BUILD_SCRIPT}'} + config = {"path": f"foo/{DEFAULT_BUILD_SCRIPT}"} - file_path = temp_dir / 'foo' / DEFAULT_BUILD_SCRIPT + file_path = temp_dir / "foo" / DEFAULT_BUILD_SCRIPT file_path.ensure_parent_dir_exists() file_path.write_text( helpers.dedent( @@ -97,10 +97,13 @@ class CustomHook: ) ) - with pytest.raises( - ValueError, - match=re.escape( - f'Unable to find a subclass of `MetadataHookInterface` in `foo/{DEFAULT_BUILD_SCRIPT}`: {temp_dir}' + with ( + pytest.raises( + ValueError, + match=re.escape( + f"Unable to find a subclass of `MetadataHookInterface` in `foo/{DEFAULT_BUILD_SCRIPT}`: {temp_dir}" + ), ), - ), temp_dir.as_cwd(): + temp_dir.as_cwd(), + ): CustomMetadataHook(str(temp_dir), config) diff --git a/tests/backend/metadata/test_hatch.py b/tests/backend/metadata/test_hatch.py index f76cd6838..1a65f27fc 100644 --- a/tests/backend/metadata/test_hatch.py +++ b/tests/backend/metadata/test_hatch.py @@ -14,17 +14,17 @@ def test_default(self, isolation): assert metadata.build_config == metadata.build_config == {} def test_not_table(self, isolation): - config = {'build': 0} + config = {"build": 0} metadata = HatchMetadata(str(isolation), config, None) - with pytest.raises(TypeError, match='Field `tool.hatch.build` must be a table'): + with pytest.raises(TypeError, match="Field `tool.hatch.build` must be a table"): _ = metadata.build_config def test_correct(self, isolation): - config = {'build': {'reproducible': True}} + config = {"build": {"reproducible": True}} metadata = HatchMetadata(str(isolation), config, None) - assert metadata.build_config == metadata.build_config == {'reproducible': True} + assert metadata.build_config == metadata.build_config == {"reproducible": True} class TestBuildTargets: @@ -35,70 +35,70 @@ def test_default(self, isolation): assert metadata.build_targets == metadata.build_targets == {} def test_not_table(self, isolation): - config = {'build': {'targets': 0}} + config = {"build": {"targets": 0}} metadata = HatchMetadata(str(isolation), config, None) - with pytest.raises(TypeError, match='Field `tool.hatch.build.targets` must be a table'): + with pytest.raises(TypeError, match="Field `tool.hatch.build.targets` must be a table"): _ = metadata.build_targets def test_correct(self, isolation): - config = {'build': {'targets': {'wheel': {'versions': ['standard']}}}} + config = {"build": {"targets": {"wheel": {"versions": ["standard"]}}}} metadata = HatchMetadata(str(isolation), config, None) - assert metadata.build_targets == metadata.build_targets == {'wheel': {'versions': ['standard']}} + assert metadata.build_targets == metadata.build_targets == {"wheel": {"versions": ["standard"]}} class TestVersionSourceName: def test_empty(self, isolation): with pytest.raises( - ValueError, match='The `source` option under the `tool.hatch.version` table must not be empty if defined' + ValueError, match="The `source` option under the `tool.hatch.version` table must not be empty if defined" ): - _ = HatchMetadata(isolation, {'version': {'source': ''}}, None).version.source_name + _ = HatchMetadata(isolation, {"version": {"source": ""}}, None).version.source_name def test_not_table(self, isolation): - with pytest.raises(TypeError, match='Field `tool.hatch.version.source` must be a string'): - _ = HatchMetadata(isolation, {'version': {'source': 9000}}, None).version.source_name + with pytest.raises(TypeError, match="Field `tool.hatch.version.source` must be a string"): + _ = HatchMetadata(isolation, {"version": {"source": 9000}}, None).version.source_name def test_correct(self, isolation): - metadata = HatchMetadata(isolation, {'version': {'source': 'foo'}}, None) + metadata = HatchMetadata(isolation, {"version": {"source": "foo"}}, None) - assert metadata.version.source_name == metadata.version.source_name == 'foo' + assert metadata.version.source_name == metadata.version.source_name == "foo" def test_default(self, isolation): - metadata = HatchMetadata(isolation, {'version': {}}, None) + metadata = HatchMetadata(isolation, {"version": {}}, None) - assert metadata.version.source_name == metadata.version.source_name == 'regex' + assert metadata.version.source_name == metadata.version.source_name == "regex" class TestVersionSchemeName: def test_missing(self, isolation): with pytest.raises( - ValueError, match='The `scheme` option under the `tool.hatch.version` table must not be empty if defined' + ValueError, match="The `scheme` option under the `tool.hatch.version` table must not be empty if defined" ): - _ = HatchMetadata(isolation, {'version': {'scheme': ''}}, None).version.scheme_name + _ = HatchMetadata(isolation, {"version": {"scheme": ""}}, None).version.scheme_name def test_not_table(self, isolation): - with pytest.raises(TypeError, match='Field `tool.hatch.version.scheme` must be a string'): - _ = HatchMetadata(isolation, {'version': {'scheme': 9000}}, None).version.scheme_name + with pytest.raises(TypeError, match="Field `tool.hatch.version.scheme` must be a string"): + _ = HatchMetadata(isolation, {"version": {"scheme": 9000}}, None).version.scheme_name def test_correct(self, isolation): - metadata = HatchMetadata(isolation, {'version': {'scheme': 'foo'}}, None) + metadata = HatchMetadata(isolation, {"version": {"scheme": "foo"}}, None) - assert metadata.version.scheme_name == metadata.version.scheme_name == 'foo' + assert metadata.version.scheme_name == metadata.version.scheme_name == "foo" def test_default(self, isolation): - metadata = HatchMetadata(isolation, {'version': {}}, None) + metadata = HatchMetadata(isolation, {"version": {}}, None) - assert metadata.version.scheme_name == metadata.version.scheme_name == 'standard' + assert metadata.version.scheme_name == metadata.version.scheme_name == "standard" class TestVersionSource: def test_unknown(self, isolation): - with pytest.raises(ValueError, match='Unknown version source: foo'): - _ = HatchMetadata(isolation, {'version': {'source': 'foo'}}, PluginManager()).version.source + with pytest.raises(ValueError, match="Unknown version source: foo"): + _ = HatchMetadata(isolation, {"version": {"source": "foo"}}, PluginManager()).version.source def test_cached(self, isolation): - metadata = HatchMetadata(isolation, {'version': {}}, PluginManager()) + metadata = HatchMetadata(isolation, {"version": {}}, PluginManager()) assert metadata.version.source is metadata.version.source assert isinstance(metadata.version.source, RegexSource) @@ -106,11 +106,11 @@ def test_cached(self, isolation): class TestVersionScheme: def test_unknown(self, isolation): - with pytest.raises(ValueError, match='Unknown version scheme: foo'): - _ = HatchMetadata(isolation, {'version': {'scheme': 'foo'}}, PluginManager()).version.scheme + with pytest.raises(ValueError, match="Unknown version scheme: foo"): + _ = HatchMetadata(isolation, {"version": {"scheme": "foo"}}, PluginManager()).version.scheme def test_cached(self, isolation): - metadata = HatchMetadata(isolation, {'version': {}}, PluginManager()) + metadata = HatchMetadata(isolation, {"version": {}}, PluginManager()) assert metadata.version.scheme is metadata.version.scheme assert isinstance(metadata.version.scheme, StandardScheme) @@ -124,17 +124,17 @@ def test_default(self, isolation): assert metadata.metadata.config == metadata.metadata.config == {} def test_not_table(self, isolation): - config = {'metadata': 0} + config = {"metadata": 0} metadata = HatchMetadata(str(isolation), config, None) - with pytest.raises(TypeError, match='Field `tool.hatch.metadata` must be a table'): + with pytest.raises(TypeError, match="Field `tool.hatch.metadata` must be a table"): _ = metadata.metadata.config def test_correct(self, isolation): - config = {'metadata': {'option': True}} + config = {"metadata": {"option": True}} metadata = HatchMetadata(str(isolation), config, None) - assert metadata.metadata.config == metadata.metadata.config == {'option': True} + assert metadata.metadata.config == metadata.metadata.config == {"option": True} class TestMetadataAllowDirectReferences: @@ -145,14 +145,14 @@ def test_default(self, isolation): assert metadata.metadata.allow_direct_references is metadata.metadata.allow_direct_references is False def test_not_boolean(self, isolation): - config = {'metadata': {'allow-direct-references': 9000}} + config = {"metadata": {"allow-direct-references": 9000}} metadata = HatchMetadata(str(isolation), config, None) - with pytest.raises(TypeError, match='Field `tool.hatch.metadata.allow-direct-references` must be a boolean'): + with pytest.raises(TypeError, match="Field `tool.hatch.metadata.allow-direct-references` must be a boolean"): _ = metadata.metadata.allow_direct_references def test_correct(self, isolation): - config = {'metadata': {'allow-direct-references': True}} + config = {"metadata": {"allow-direct-references": True}} metadata = HatchMetadata(str(isolation), config, None) assert metadata.metadata.allow_direct_references is True @@ -166,14 +166,14 @@ def test_default(self, isolation): assert metadata.metadata.allow_ambiguous_features is metadata.metadata.allow_ambiguous_features is False def test_not_boolean(self, isolation): - config = {'metadata': {'allow-ambiguous-features': 9000}} + config = {"metadata": {"allow-ambiguous-features": 9000}} metadata = HatchMetadata(str(isolation), config, None) - with pytest.raises(TypeError, match='Field `tool.hatch.metadata.allow-ambiguous-features` must be a boolean'): + with pytest.raises(TypeError, match="Field `tool.hatch.metadata.allow-ambiguous-features` must be a boolean"): _ = metadata.metadata.allow_ambiguous_features def test_correct(self, isolation): - config = {'metadata': {'allow-ambiguous-features': True}} + config = {"metadata": {"allow-ambiguous-features": True}} metadata = HatchMetadata(str(isolation), config, None) assert metadata.metadata.allow_ambiguous_features is True diff --git a/tests/backend/metadata/test_spec.py b/tests/backend/metadata/test_spec.py index 5948b55fe..ea47bf06e 100644 --- a/tests/backend/metadata/test_spec.py +++ b/tests/backend/metadata/test_spec.py @@ -13,7 +13,7 @@ def test_missing_name(self): core_metadata = f"""\ Metadata-Version: {LATEST_METADATA_VERSION} """ - with pytest.raises(ValueError, match='^Missing required core metadata: Name$'): + with pytest.raises(ValueError, match="^Missing required core metadata: Name$"): project_metadata_from_core_metadata(core_metadata) def test_missing_version(self): @@ -21,7 +21,7 @@ def test_missing_version(self): Metadata-Version: {LATEST_METADATA_VERSION} Name: My.App """ - with pytest.raises(ValueError, match='^Missing required core metadata: Version$'): + with pytest.raises(ValueError, match="^Missing required core metadata: Version$"): project_metadata_from_core_metadata(core_metadata) def test_dynamic(self): @@ -33,9 +33,9 @@ def test_dynamic(self): Dynamic: Provides-Extra """ assert project_metadata_from_core_metadata(core_metadata) == { - 'name': 'My.App', - 'version': '0.1.0', - 'dynamic': ['classifiers', 'dependencies', 'optional-dependencies'], + "name": "My.App", + "version": "0.1.0", + "dynamic": ["classifiers", "dependencies", "optional-dependencies"], } def test_description(self): @@ -46,9 +46,9 @@ def test_description(self): Summary: foo """ assert project_metadata_from_core_metadata(core_metadata) == { - 'name': 'My.App', - 'version': '0.1.0', - 'description': 'foo', + "name": "My.App", + "version": "0.1.0", + "description": "foo", } def test_urls(self): @@ -60,9 +60,9 @@ def test_urls(self): Project-URL: bar, baz """ assert project_metadata_from_core_metadata(core_metadata) == { - 'name': 'My.App', - 'version': '0.1.0', - 'urls': {'foo': 'bar', 'bar': 'baz'}, + "name": "My.App", + "version": "0.1.0", + "urls": {"foo": "bar", "bar": "baz"}, } def test_authors(self): @@ -74,9 +74,9 @@ def test_authors(self): Author-email: foo , """ assert project_metadata_from_core_metadata(core_metadata) == { - 'name': 'My.App', - 'version': '0.1.0', - 'authors': [{'name': 'foobar'}, {'email': 'bar@domain', 'name': 'foo'}, {'email': 'baz@domain'}], + "name": "My.App", + "version": "0.1.0", + "authors": [{"name": "foobar"}, {"email": "bar@domain", "name": "foo"}, {"email": "baz@domain"}], } def test_maintainers(self): @@ -88,9 +88,9 @@ def test_maintainers(self): Maintainer-email: foo , """ assert project_metadata_from_core_metadata(core_metadata) == { - 'name': 'My.App', - 'version': '0.1.0', - 'maintainers': [{'name': 'foobar'}, {'email': 'bar@domain', 'name': 'foo'}, {'email': 'baz@domain'}], + "name": "My.App", + "version": "0.1.0", + "maintainers": [{"name": "foobar"}, {"email": "bar@domain", "name": "foo"}, {"email": "baz@domain"}], } def test_keywords(self): @@ -101,9 +101,9 @@ def test_keywords(self): Keywords: bar,foo """ assert project_metadata_from_core_metadata(core_metadata) == { - 'name': 'My.App', - 'version': '0.1.0', - 'keywords': ['bar', 'foo'], + "name": "My.App", + "version": "0.1.0", + "keywords": ["bar", "foo"], } def test_classifiers(self): @@ -115,9 +115,9 @@ def test_classifiers(self): Classifier: Programming Language :: Python :: 3.11 """ assert project_metadata_from_core_metadata(core_metadata) == { - 'name': 'My.App', - 'version': '0.1.0', - 'classifiers': ['Programming Language :: Python :: 3.9', 'Programming Language :: Python :: 3.11'], + "name": "My.App", + "version": "0.1.0", + "classifiers": ["Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.11"], } def test_license_files(self): @@ -129,9 +129,9 @@ def test_license_files(self): License-File: LICENSES/MIT.txt """ assert project_metadata_from_core_metadata(core_metadata) == { - 'name': 'My.App', - 'version': '0.1.0', - 'license-files': ['LICENSES/Apache-2.0.txt', 'LICENSES/MIT.txt'], + "name": "My.App", + "version": "0.1.0", + "license-files": ["LICENSES/Apache-2.0.txt", "LICENSES/MIT.txt"], } def test_license_expression(self): @@ -142,9 +142,9 @@ def test_license_expression(self): License-Expression: MIT """ assert project_metadata_from_core_metadata(core_metadata) == { - 'name': 'My.App', - 'version': '0.1.0', - 'license': 'MIT', + "name": "My.App", + "version": "0.1.0", + "license": "MIT", } def test_license_legacy(self): @@ -155,9 +155,9 @@ def test_license_legacy(self): License: foo """ assert project_metadata_from_core_metadata(core_metadata) == { - 'name': 'My.App', - 'version': '0.1.0', - 'license': {'text': 'foo'}, + "name": "My.App", + "version": "0.1.0", + "license": {"text": "foo"}, } def test_readme(self): @@ -170,9 +170,9 @@ def test_readme(self): test content """ assert project_metadata_from_core_metadata(core_metadata) == { - 'name': 'My.App', - 'version': '0.1.0', - 'readme': {'content-type': 'text/markdown', 'text': 'test content\n'}, + "name": "My.App", + "version": "0.1.0", + "readme": {"content-type": "text/markdown", "text": "test content\n"}, } def test_readme_default_content_type(self): @@ -184,9 +184,9 @@ def test_readme_default_content_type(self): test content """ assert project_metadata_from_core_metadata(core_metadata) == { - 'name': 'My.App', - 'version': '0.1.0', - 'readme': {'content-type': 'text/plain', 'text': 'test content\n'}, + "name": "My.App", + "version": "0.1.0", + "readme": {"content-type": "text/plain", "text": "test content\n"}, } def test_requires_python(self): @@ -197,9 +197,9 @@ def test_requires_python(self): Requires-Python: <2,>=1 """ assert project_metadata_from_core_metadata(core_metadata) == { - 'name': 'My.App', - 'version': '0.1.0', - 'requires-python': '<2,>=1', + "name": "My.App", + "version": "0.1.0", + "requires-python": "<2,>=1", } def test_dependencies(self): @@ -219,21 +219,21 @@ def test_dependencies(self): Requires-Dist: baz@ file:///path/to/project ; extra == 'feature3' """ assert project_metadata_from_core_metadata(core_metadata) == { - 'name': 'My.App', - 'version': '0.1.0', - 'dependencies': ['bar==5', 'foo==1'], - 'optional-dependencies': { - 'feature1': ['bar==5; python_version < "3"', 'foo==1'], - 'feature2': ['bar==5', 'foo==1; python_version < "3"'], - 'feature3': ['baz@ file:///path/to/project'], + "name": "My.App", + "version": "0.1.0", + "dependencies": ["bar==5", "foo==1"], + "optional-dependencies": { + "feature1": ['bar==5; python_version < "3"', "foo==1"], + "feature2": ["bar==5", 'foo==1; python_version < "3"'], + "feature3": ["baz@ file:///path/to/project"], }, } -@pytest.mark.parametrize('constructor', [get_core_metadata_constructors()['1.2']]) +@pytest.mark.parametrize("constructor", [get_core_metadata_constructors()["1.2"]]) class TestCoreMetadataV12: def test_default(self, constructor, isolation, helpers): - metadata = ProjectMetadata(str(isolation), None, {'project': {'name': 'My.App', 'version': '0.1.0'}}) + metadata = ProjectMetadata(str(isolation), None, {"project": {"name": "My.App", "version": "0.1.0"}}) assert constructor(metadata) == helpers.dedent( """ @@ -245,7 +245,7 @@ def test_default(self, constructor, isolation, helpers): def test_description(self, constructor, isolation, helpers): metadata = ProjectMetadata( - str(isolation), None, {'project': {'name': 'My.App', 'version': '0.1.0', 'description': 'foo'}} + str(isolation), None, {"project": {"name": "My.App", "version": "0.1.0", "description": "foo"}} ) assert constructor(metadata) == helpers.dedent( @@ -261,7 +261,7 @@ def test_urls(self, constructor, isolation, helpers): metadata = ProjectMetadata( str(isolation), None, - {'project': {'name': 'My.App', 'version': '0.1.0', 'urls': {'foo': 'bar', 'bar': 'baz'}}}, + {"project": {"name": "My.App", "version": "0.1.0", "urls": {"foo": "bar", "bar": "baz"}}}, ) assert constructor(metadata) == helpers.dedent( @@ -276,7 +276,7 @@ def test_urls(self, constructor, isolation, helpers): def test_authors_name(self, constructor, isolation, helpers): metadata = ProjectMetadata( - str(isolation), None, {'project': {'name': 'My.App', 'version': '0.1.0', 'authors': [{'name': 'foo'}]}} + str(isolation), None, {"project": {"name": "My.App", "version": "0.1.0", "authors": [{"name": "foo"}]}} ) assert constructor(metadata) == helpers.dedent( @@ -292,7 +292,7 @@ def test_authors_email(self, constructor, isolation, helpers): metadata = ProjectMetadata( str(isolation), None, - {'project': {'name': 'My.App', 'version': '0.1.0', 'authors': [{'email': 'foo@domain'}]}}, + {"project": {"name": "My.App", "version": "0.1.0", "authors": [{"email": "foo@domain"}]}}, ) assert constructor(metadata) == helpers.dedent( @@ -308,7 +308,7 @@ def test_authors_name_and_email(self, constructor, isolation, helpers): metadata = ProjectMetadata( str(isolation), None, - {'project': {'name': 'My.App', 'version': '0.1.0', 'authors': [{'email': 'bar@domain', 'name': 'foo'}]}}, + {"project": {"name": "My.App", "version": "0.1.0", "authors": [{"email": "bar@domain", "name": "foo"}]}}, ) assert constructor(metadata) == helpers.dedent( @@ -324,7 +324,7 @@ def test_authors_multiple(self, constructor, isolation, helpers): metadata = ProjectMetadata( str(isolation), None, - {'project': {'name': 'My.App', 'version': '0.1.0', 'authors': [{'name': 'foo'}, {'name': 'bar'}]}}, + {"project": {"name": "My.App", "version": "0.1.0", "authors": [{"name": "foo"}, {"name": "bar"}]}}, ) assert constructor(metadata) == helpers.dedent( @@ -338,7 +338,7 @@ def test_authors_multiple(self, constructor, isolation, helpers): def test_maintainers_name(self, constructor, isolation, helpers): metadata = ProjectMetadata( - str(isolation), None, {'project': {'name': 'My.App', 'version': '0.1.0', 'maintainers': [{'name': 'foo'}]}} + str(isolation), None, {"project": {"name": "My.App", "version": "0.1.0", "maintainers": [{"name": "foo"}]}} ) assert constructor(metadata) == helpers.dedent( @@ -354,7 +354,7 @@ def test_maintainers_email(self, constructor, isolation, helpers): metadata = ProjectMetadata( str(isolation), None, - {'project': {'name': 'My.App', 'version': '0.1.0', 'maintainers': [{'email': 'foo@domain'}]}}, + {"project": {"name": "My.App", "version": "0.1.0", "maintainers": [{"email": "foo@domain"}]}}, ) assert constructor(metadata) == helpers.dedent( @@ -371,10 +371,10 @@ def test_maintainers_name_and_email(self, constructor, isolation, helpers): str(isolation), None, { - 'project': { - 'name': 'My.App', - 'version': '0.1.0', - 'maintainers': [{'email': 'bar@domain', 'name': 'foo'}], + "project": { + "name": "My.App", + "version": "0.1.0", + "maintainers": [{"email": "bar@domain", "name": "foo"}], } }, ) @@ -392,7 +392,7 @@ def test_maintainers_multiple(self, constructor, isolation, helpers): metadata = ProjectMetadata( str(isolation), None, - {'project': {'name': 'My.App', 'version': '0.1.0', 'maintainers': [{'name': 'foo'}, {'name': 'bar'}]}}, + {"project": {"name": "My.App", "version": "0.1.0", "maintainers": [{"name": "foo"}, {"name": "bar"}]}}, ) assert constructor(metadata) == helpers.dedent( @@ -406,7 +406,7 @@ def test_maintainers_multiple(self, constructor, isolation, helpers): def test_license(self, constructor, isolation, helpers): metadata = ProjectMetadata( - str(isolation), None, {'project': {'name': 'My.App', 'version': '0.1.0', 'license': {'text': 'foo\nbar'}}} + str(isolation), None, {"project": {"name": "My.App", "version": "0.1.0", "license": {"text": "foo\nbar"}}} ) assert constructor(metadata) == helpers.dedent( @@ -423,7 +423,7 @@ def test_license_expression(self, constructor, isolation, helpers): metadata = ProjectMetadata( str(isolation), None, - {'project': {'name': 'My.App', 'version': '0.1.0', 'license': 'mit'}}, + {"project": {"name": "My.App", "version": "0.1.0", "license": "mit"}}, ) assert constructor(metadata) == helpers.dedent( @@ -437,7 +437,7 @@ def test_license_expression(self, constructor, isolation, helpers): def test_keywords_single(self, constructor, isolation, helpers): metadata = ProjectMetadata( - str(isolation), None, {'project': {'name': 'My.App', 'version': '0.1.0', 'keywords': ['foo']}} + str(isolation), None, {"project": {"name": "My.App", "version": "0.1.0", "keywords": ["foo"]}} ) assert constructor(metadata) == helpers.dedent( @@ -451,7 +451,7 @@ def test_keywords_single(self, constructor, isolation, helpers): def test_keywords_multiple(self, constructor, isolation, helpers): metadata = ProjectMetadata( - str(isolation), None, {'project': {'name': 'My.App', 'version': '0.1.0', 'keywords': ['foo', 'bar']}} + str(isolation), None, {"project": {"name": "My.App", "version": "0.1.0", "keywords": ["foo", "bar"]}} ) assert constructor(metadata) == helpers.dedent( @@ -465,11 +465,11 @@ def test_keywords_multiple(self, constructor, isolation, helpers): def test_classifiers(self, constructor, isolation, helpers): classifiers = [ - 'Programming Language :: Python :: 3.11', - 'Programming Language :: Python :: 3.9', + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.9", ] metadata = ProjectMetadata( - str(isolation), None, {'project': {'name': 'My.App', 'version': '0.1.0', 'classifiers': classifiers}} + str(isolation), None, {"project": {"name": "My.App", "version": "0.1.0", "classifiers": classifiers}} ) assert constructor(metadata) == helpers.dedent( @@ -484,7 +484,7 @@ def test_classifiers(self, constructor, isolation, helpers): def test_requires_python(self, constructor, isolation, helpers): metadata = ProjectMetadata( - str(isolation), None, {'project': {'name': 'My.App', 'version': '0.1.0', 'requires-python': '>=1,<2'}} + str(isolation), None, {"project": {"name": "My.App", "version": "0.1.0", "requires-python": ">=1,<2"}} ) assert constructor(metadata) == helpers.dedent( @@ -500,7 +500,7 @@ def test_dependencies(self, constructor, isolation, helpers): metadata = ProjectMetadata( str(isolation), None, - {'project': {'name': 'My.App', 'version': '0.1.0', 'dependencies': ['foo==1', 'bar==5']}}, + {"project": {"name": "My.App", "version": "0.1.0", "dependencies": ["foo==1", "bar==5"]}}, ) assert constructor(metadata) == helpers.dedent( @@ -517,10 +517,10 @@ def test_extra_runtime_dependencies(self, constructor, isolation, helpers): metadata = ProjectMetadata( str(isolation), None, - {'project': {'name': 'My.App', 'version': '0.1.0', 'dependencies': ['foo==1', 'bar==5']}}, + {"project": {"name": "My.App", "version": "0.1.0", "dependencies": ["foo==1", "bar==5"]}}, ) - assert constructor(metadata, extra_dependencies=['baz==9']) == helpers.dedent( + assert constructor(metadata, extra_dependencies=["baz==9"]) == helpers.dedent( """ Metadata-Version: 1.2 Name: My.App @@ -536,21 +536,21 @@ def test_all(self, constructor, isolation, helpers): str(isolation), None, { - 'project': { - 'name': 'My.App', - 'version': '0.1.0', - 'description': 'foo', - 'urls': {'foo': 'bar', 'bar': 'baz'}, - 'authors': [{'email': 'bar@domain', 'name': 'foo'}], - 'maintainers': [{'email': 'bar@domain', 'name': 'foo'}], - 'license': {'text': 'foo\nbar'}, - 'keywords': ['foo', 'bar'], - 'classifiers': [ - 'Programming Language :: Python :: 3.11', - 'Programming Language :: Python :: 3.9', + "project": { + "name": "My.App", + "version": "0.1.0", + "description": "foo", + "urls": {"foo": "bar", "bar": "baz"}, + "authors": [{"email": "bar@domain", "name": "foo"}], + "maintainers": [{"email": "bar@domain", "name": "foo"}], + "license": {"text": "foo\nbar"}, + "keywords": ["foo", "bar"], + "classifiers": [ + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.9", ], - 'requires-python': '>=1,<2', - 'dependencies': ['foo==1', 'bar==5'], + "requires-python": ">=1,<2", + "dependencies": ["foo==1", "bar==5"], } }, ) @@ -577,10 +577,10 @@ def test_all(self, constructor, isolation, helpers): ) -@pytest.mark.parametrize('constructor', [get_core_metadata_constructors()['2.1']]) +@pytest.mark.parametrize("constructor", [get_core_metadata_constructors()["2.1"]]) class TestCoreMetadataV21: def test_default(self, constructor, isolation, helpers): - metadata = ProjectMetadata(str(isolation), None, {'project': {'name': 'My.App', 'version': '0.1.0'}}) + metadata = ProjectMetadata(str(isolation), None, {"project": {"name": "My.App", "version": "0.1.0"}}) assert constructor(metadata) == helpers.dedent( """ @@ -592,7 +592,7 @@ def test_default(self, constructor, isolation, helpers): def test_description(self, constructor, isolation, helpers): metadata = ProjectMetadata( - str(isolation), None, {'project': {'name': 'My.App', 'version': '0.1.0', 'description': 'foo'}} + str(isolation), None, {"project": {"name": "My.App", "version": "0.1.0", "description": "foo"}} ) assert constructor(metadata) == helpers.dedent( @@ -608,7 +608,7 @@ def test_urls(self, constructor, isolation, helpers): metadata = ProjectMetadata( str(isolation), None, - {'project': {'name': 'My.App', 'version': '0.1.0', 'urls': {'foo': 'bar', 'bar': 'baz'}}}, + {"project": {"name": "My.App", "version": "0.1.0", "urls": {"foo": "bar", "bar": "baz"}}}, ) assert constructor(metadata) == helpers.dedent( @@ -623,7 +623,7 @@ def test_urls(self, constructor, isolation, helpers): def test_authors_name(self, constructor, isolation, helpers): metadata = ProjectMetadata( - str(isolation), None, {'project': {'name': 'My.App', 'version': '0.1.0', 'authors': [{'name': 'foo'}]}} + str(isolation), None, {"project": {"name": "My.App", "version": "0.1.0", "authors": [{"name": "foo"}]}} ) assert constructor(metadata) == helpers.dedent( @@ -639,7 +639,7 @@ def test_authors_email(self, constructor, isolation, helpers): metadata = ProjectMetadata( str(isolation), None, - {'project': {'name': 'My.App', 'version': '0.1.0', 'authors': [{'email': 'foo@domain'}]}}, + {"project": {"name": "My.App", "version": "0.1.0", "authors": [{"email": "foo@domain"}]}}, ) assert constructor(metadata) == helpers.dedent( @@ -655,7 +655,7 @@ def test_authors_name_and_email(self, constructor, isolation, helpers): metadata = ProjectMetadata( str(isolation), None, - {'project': {'name': 'My.App', 'version': '0.1.0', 'authors': [{'email': 'bar@domain', 'name': 'foo'}]}}, + {"project": {"name": "My.App", "version": "0.1.0", "authors": [{"email": "bar@domain", "name": "foo"}]}}, ) assert constructor(metadata) == helpers.dedent( @@ -671,7 +671,7 @@ def test_authors_multiple(self, constructor, isolation, helpers): metadata = ProjectMetadata( str(isolation), None, - {'project': {'name': 'My.App', 'version': '0.1.0', 'authors': [{'name': 'foo'}, {'name': 'bar'}]}}, + {"project": {"name": "My.App", "version": "0.1.0", "authors": [{"name": "foo"}, {"name": "bar"}]}}, ) assert constructor(metadata) == helpers.dedent( @@ -685,7 +685,7 @@ def test_authors_multiple(self, constructor, isolation, helpers): def test_maintainers_name(self, constructor, isolation, helpers): metadata = ProjectMetadata( - str(isolation), None, {'project': {'name': 'My.App', 'version': '0.1.0', 'maintainers': [{'name': 'foo'}]}} + str(isolation), None, {"project": {"name": "My.App", "version": "0.1.0", "maintainers": [{"name": "foo"}]}} ) assert constructor(metadata) == helpers.dedent( @@ -701,7 +701,7 @@ def test_maintainers_email(self, constructor, isolation, helpers): metadata = ProjectMetadata( str(isolation), None, - {'project': {'name': 'My.App', 'version': '0.1.0', 'maintainers': [{'email': 'foo@domain'}]}}, + {"project": {"name": "My.App", "version": "0.1.0", "maintainers": [{"email": "foo@domain"}]}}, ) assert constructor(metadata) == helpers.dedent( @@ -718,10 +718,10 @@ def test_maintainers_name_and_email(self, constructor, isolation, helpers): str(isolation), None, { - 'project': { - 'name': 'My.App', - 'version': '0.1.0', - 'maintainers': [{'email': 'bar@domain', 'name': 'foo'}], + "project": { + "name": "My.App", + "version": "0.1.0", + "maintainers": [{"email": "bar@domain", "name": "foo"}], } }, ) @@ -739,7 +739,7 @@ def test_maintainers_multiple(self, constructor, isolation, helpers): metadata = ProjectMetadata( str(isolation), None, - {'project': {'name': 'My.App', 'version': '0.1.0', 'maintainers': [{'name': 'foo'}, {'name': 'bar'}]}}, + {"project": {"name": "My.App", "version": "0.1.0", "maintainers": [{"name": "foo"}, {"name": "bar"}]}}, ) assert constructor(metadata) == helpers.dedent( @@ -753,7 +753,7 @@ def test_maintainers_multiple(self, constructor, isolation, helpers): def test_license(self, constructor, isolation, helpers): metadata = ProjectMetadata( - str(isolation), None, {'project': {'name': 'My.App', 'version': '0.1.0', 'license': {'text': 'foo\nbar'}}} + str(isolation), None, {"project": {"name": "My.App", "version": "0.1.0", "license": {"text": "foo\nbar"}}} ) assert constructor(metadata) == helpers.dedent( @@ -770,7 +770,7 @@ def test_license_expression(self, constructor, isolation, helpers): metadata = ProjectMetadata( str(isolation), None, - {'project': {'name': 'My.App', 'version': '0.1.0', 'license': 'mit'}}, + {"project": {"name": "My.App", "version": "0.1.0", "license": "mit"}}, ) assert constructor(metadata) == helpers.dedent( @@ -784,7 +784,7 @@ def test_license_expression(self, constructor, isolation, helpers): def test_keywords_single(self, constructor, isolation, helpers): metadata = ProjectMetadata( - str(isolation), None, {'project': {'name': 'My.App', 'version': '0.1.0', 'keywords': ['foo']}} + str(isolation), None, {"project": {"name": "My.App", "version": "0.1.0", "keywords": ["foo"]}} ) assert constructor(metadata) == helpers.dedent( @@ -798,7 +798,7 @@ def test_keywords_single(self, constructor, isolation, helpers): def test_keywords_multiple(self, constructor, isolation, helpers): metadata = ProjectMetadata( - str(isolation), None, {'project': {'name': 'My.App', 'version': '0.1.0', 'keywords': ['foo', 'bar']}} + str(isolation), None, {"project": {"name": "My.App", "version": "0.1.0", "keywords": ["foo", "bar"]}} ) assert constructor(metadata) == helpers.dedent( @@ -812,11 +812,11 @@ def test_keywords_multiple(self, constructor, isolation, helpers): def test_classifiers(self, constructor, isolation, helpers): classifiers = [ - 'Programming Language :: Python :: 3.11', - 'Programming Language :: Python :: 3.9', + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.9", ] metadata = ProjectMetadata( - str(isolation), None, {'project': {'name': 'My.App', 'version': '0.1.0', 'classifiers': classifiers}} + str(isolation), None, {"project": {"name": "My.App", "version": "0.1.0", "classifiers": classifiers}} ) assert constructor(metadata) == helpers.dedent( @@ -831,7 +831,7 @@ def test_classifiers(self, constructor, isolation, helpers): def test_requires_python(self, constructor, isolation, helpers): metadata = ProjectMetadata( - str(isolation), None, {'project': {'name': 'My.App', 'version': '0.1.0', 'requires-python': '>=1,<2'}} + str(isolation), None, {"project": {"name": "My.App", "version": "0.1.0", "requires-python": ">=1,<2"}} ) assert constructor(metadata) == helpers.dedent( @@ -847,7 +847,7 @@ def test_dependencies(self, constructor, isolation, helpers): metadata = ProjectMetadata( str(isolation), None, - {'project': {'name': 'My.App', 'version': '0.1.0', 'dependencies': ['foo==1', 'bar==5']}}, + {"project": {"name": "My.App", "version": "0.1.0", "dependencies": ["foo==1", "bar==5"]}}, ) assert constructor(metadata) == helpers.dedent( @@ -865,12 +865,12 @@ def test_optional_dependencies(self, constructor, isolation, helpers): str(isolation), None, { - 'project': { - 'name': 'My.App', - 'version': '0.1.0', - 'optional-dependencies': { - 'feature2': ['foo==1; python_version < "3"', 'bar==5'], - 'feature1': ['foo==1', 'bar==5; python_version < "3"'], + "project": { + "name": "My.App", + "version": "0.1.0", + "optional-dependencies": { + "feature2": ['foo==1; python_version < "3"', "bar==5"], + "feature1": ["foo==1", 'bar==5; python_version < "3"'], }, } }, @@ -894,10 +894,10 @@ def test_extra_runtime_dependencies(self, constructor, isolation, helpers): metadata = ProjectMetadata( str(isolation), None, - {'project': {'name': 'My.App', 'version': '0.1.0', 'dependencies': ['foo==1', 'bar==5']}}, + {"project": {"name": "My.App", "version": "0.1.0", "dependencies": ["foo==1", "bar==5"]}}, ) - assert constructor(metadata, extra_dependencies=['baz==9']) == helpers.dedent( + assert constructor(metadata, extra_dependencies=["baz==9"]) == helpers.dedent( """ Metadata-Version: 2.1 Name: My.App @@ -913,10 +913,10 @@ def test_readme(self, constructor, isolation, helpers): str(isolation), None, { - 'project': { - 'name': 'My.App', - 'version': '0.1.0', - 'readme': {'content-type': 'text/markdown', 'text': 'test content\n'}, + "project": { + "name": "My.App", + "version": "0.1.0", + "readme": {"content-type": "text/markdown", "text": "test content\n"}, } }, ) @@ -937,33 +937,33 @@ def test_all(self, constructor, helpers, temp_dir): str(temp_dir), None, { - 'project': { - 'name': 'My.App', - 'version': '0.1.0', - 'description': 'foo', - 'urls': {'foo': 'bar', 'bar': 'baz'}, - 'authors': [{'email': 'bar@domain', 'name': 'foo'}], - 'maintainers': [{'email': 'bar@domain', 'name': 'foo'}], - 'license': {'text': 'foo\nbar'}, - 'keywords': ['foo', 'bar'], - 'classifiers': [ - 'Programming Language :: Python :: 3.11', - 'Programming Language :: Python :: 3.9', + "project": { + "name": "My.App", + "version": "0.1.0", + "description": "foo", + "urls": {"foo": "bar", "bar": "baz"}, + "authors": [{"email": "bar@domain", "name": "foo"}], + "maintainers": [{"email": "bar@domain", "name": "foo"}], + "license": {"text": "foo\nbar"}, + "keywords": ["foo", "bar"], + "classifiers": [ + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.9", ], - 'requires-python': '>=1,<2', - 'dependencies': ['foo==1', 'bar==5'], - 'optional-dependencies': { - 'feature2': ['foo==1; python_version < "3"', 'bar==5'], - 'feature1': ['foo==1', 'bar==5; python_version < "3"'], - 'feature3': ['baz @ file:///path/to/project'], + "requires-python": ">=1,<2", + "dependencies": ["foo==1", "bar==5"], + "optional-dependencies": { + "feature2": ['foo==1; python_version < "3"', "bar==5"], + "feature1": ["foo==1", 'bar==5; python_version < "3"'], + "feature3": ["baz @ file:///path/to/project"], }, - 'readme': {'content-type': 'text/markdown', 'text': 'test content\n'}, + "readme": {"content-type": "text/markdown", "text": "test content\n"}, }, - 'tool': {'hatch': {'metadata': {'allow-direct-references': True}}}, + "tool": {"hatch": {"metadata": {"allow-direct-references": True}}}, }, ) - (temp_dir / 'LICENSE.txt').touch() + (temp_dir / "LICENSE.txt").touch() assert constructor(metadata) == helpers.dedent( """ @@ -998,10 +998,10 @@ def test_all(self, constructor, helpers, temp_dir): ) -@pytest.mark.parametrize('constructor', [get_core_metadata_constructors()['2.2']]) +@pytest.mark.parametrize("constructor", [get_core_metadata_constructors()["2.2"]]) class TestCoreMetadataV22: def test_default(self, constructor, isolation, helpers): - metadata = ProjectMetadata(str(isolation), None, {'project': {'name': 'My.App', 'version': '0.1.0'}}) + metadata = ProjectMetadata(str(isolation), None, {"project": {"name": "My.App", "version": "0.1.0"}}) assert constructor(metadata) == helpers.dedent( """ @@ -1015,7 +1015,7 @@ def test_dynamic(self, constructor, isolation, helpers): metadata = ProjectMetadata( str(isolation), None, - {'project': {'name': 'My.App', 'version': '0.1.0', 'dynamic': ['authors', 'classifiers']}}, + {"project": {"name": "My.App", "version": "0.1.0", "dynamic": ["authors", "classifiers"]}}, ) assert constructor(metadata) == helpers.dedent( @@ -1031,7 +1031,7 @@ def test_dynamic(self, constructor, isolation, helpers): def test_description(self, constructor, isolation, helpers): metadata = ProjectMetadata( - str(isolation), None, {'project': {'name': 'My.App', 'version': '0.1.0', 'description': 'foo'}} + str(isolation), None, {"project": {"name": "My.App", "version": "0.1.0", "description": "foo"}} ) assert constructor(metadata) == helpers.dedent( @@ -1047,7 +1047,7 @@ def test_urls(self, constructor, isolation, helpers): metadata = ProjectMetadata( str(isolation), None, - {'project': {'name': 'My.App', 'version': '0.1.0', 'urls': {'foo': 'bar', 'bar': 'baz'}}}, + {"project": {"name": "My.App", "version": "0.1.0", "urls": {"foo": "bar", "bar": "baz"}}}, ) assert constructor(metadata) == helpers.dedent( @@ -1062,7 +1062,7 @@ def test_urls(self, constructor, isolation, helpers): def test_authors_name(self, constructor, isolation, helpers): metadata = ProjectMetadata( - str(isolation), None, {'project': {'name': 'My.App', 'version': '0.1.0', 'authors': [{'name': 'foo'}]}} + str(isolation), None, {"project": {"name": "My.App", "version": "0.1.0", "authors": [{"name": "foo"}]}} ) assert constructor(metadata) == helpers.dedent( @@ -1078,7 +1078,7 @@ def test_authors_email(self, constructor, isolation, helpers): metadata = ProjectMetadata( str(isolation), None, - {'project': {'name': 'My.App', 'version': '0.1.0', 'authors': [{'email': 'foo@domain'}]}}, + {"project": {"name": "My.App", "version": "0.1.0", "authors": [{"email": "foo@domain"}]}}, ) assert constructor(metadata) == helpers.dedent( @@ -1094,7 +1094,7 @@ def test_authors_name_and_email(self, constructor, isolation, helpers): metadata = ProjectMetadata( str(isolation), None, - {'project': {'name': 'My.App', 'version': '0.1.0', 'authors': [{'email': 'bar@domain', 'name': 'foo'}]}}, + {"project": {"name": "My.App", "version": "0.1.0", "authors": [{"email": "bar@domain", "name": "foo"}]}}, ) assert constructor(metadata) == helpers.dedent( @@ -1110,7 +1110,7 @@ def test_authors_multiple(self, constructor, isolation, helpers): metadata = ProjectMetadata( str(isolation), None, - {'project': {'name': 'My.App', 'version': '0.1.0', 'authors': [{'name': 'foo'}, {'name': 'bar'}]}}, + {"project": {"name": "My.App", "version": "0.1.0", "authors": [{"name": "foo"}, {"name": "bar"}]}}, ) assert constructor(metadata) == helpers.dedent( @@ -1124,7 +1124,7 @@ def test_authors_multiple(self, constructor, isolation, helpers): def test_maintainers_name(self, constructor, isolation, helpers): metadata = ProjectMetadata( - str(isolation), None, {'project': {'name': 'My.App', 'version': '0.1.0', 'maintainers': [{'name': 'foo'}]}} + str(isolation), None, {"project": {"name": "My.App", "version": "0.1.0", "maintainers": [{"name": "foo"}]}} ) assert constructor(metadata) == helpers.dedent( @@ -1140,7 +1140,7 @@ def test_maintainers_email(self, constructor, isolation, helpers): metadata = ProjectMetadata( str(isolation), None, - {'project': {'name': 'My.App', 'version': '0.1.0', 'maintainers': [{'email': 'foo@domain'}]}}, + {"project": {"name": "My.App", "version": "0.1.0", "maintainers": [{"email": "foo@domain"}]}}, ) assert constructor(metadata) == helpers.dedent( @@ -1157,10 +1157,10 @@ def test_maintainers_name_and_email(self, constructor, isolation, helpers): str(isolation), None, { - 'project': { - 'name': 'My.App', - 'version': '0.1.0', - 'maintainers': [{'email': 'bar@domain', 'name': 'foo'}], + "project": { + "name": "My.App", + "version": "0.1.0", + "maintainers": [{"email": "bar@domain", "name": "foo"}], } }, ) @@ -1178,7 +1178,7 @@ def test_maintainers_multiple(self, constructor, isolation, helpers): metadata = ProjectMetadata( str(isolation), None, - {'project': {'name': 'My.App', 'version': '0.1.0', 'maintainers': [{'name': 'foo'}, {'name': 'bar'}]}}, + {"project": {"name": "My.App", "version": "0.1.0", "maintainers": [{"name": "foo"}, {"name": "bar"}]}}, ) assert constructor(metadata) == helpers.dedent( @@ -1192,7 +1192,7 @@ def test_maintainers_multiple(self, constructor, isolation, helpers): def test_license(self, constructor, isolation, helpers): metadata = ProjectMetadata( - str(isolation), None, {'project': {'name': 'My.App', 'version': '0.1.0', 'license': {'text': 'foo\nbar'}}} + str(isolation), None, {"project": {"name": "My.App", "version": "0.1.0", "license": {"text": "foo\nbar"}}} ) assert constructor(metadata) == helpers.dedent( @@ -1209,7 +1209,7 @@ def test_license_expression(self, constructor, isolation, helpers): metadata = ProjectMetadata( str(isolation), None, - {'project': {'name': 'My.App', 'version': '0.1.0', 'license': 'mit'}}, + {"project": {"name": "My.App", "version": "0.1.0", "license": "mit"}}, ) assert constructor(metadata) == helpers.dedent( @@ -1223,7 +1223,7 @@ def test_license_expression(self, constructor, isolation, helpers): def test_keywords_single(self, constructor, isolation, helpers): metadata = ProjectMetadata( - str(isolation), None, {'project': {'name': 'My.App', 'version': '0.1.0', 'keywords': ['foo']}} + str(isolation), None, {"project": {"name": "My.App", "version": "0.1.0", "keywords": ["foo"]}} ) assert constructor(metadata) == helpers.dedent( @@ -1237,7 +1237,7 @@ def test_keywords_single(self, constructor, isolation, helpers): def test_keywords_multiple(self, constructor, isolation, helpers): metadata = ProjectMetadata( - str(isolation), None, {'project': {'name': 'My.App', 'version': '0.1.0', 'keywords': ['foo', 'bar']}} + str(isolation), None, {"project": {"name": "My.App", "version": "0.1.0", "keywords": ["foo", "bar"]}} ) assert constructor(metadata) == helpers.dedent( @@ -1251,11 +1251,11 @@ def test_keywords_multiple(self, constructor, isolation, helpers): def test_classifiers(self, constructor, isolation, helpers): classifiers = [ - 'Programming Language :: Python :: 3.11', - 'Programming Language :: Python :: 3.9', + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.9", ] metadata = ProjectMetadata( - str(isolation), None, {'project': {'name': 'My.App', 'version': '0.1.0', 'classifiers': classifiers}} + str(isolation), None, {"project": {"name": "My.App", "version": "0.1.0", "classifiers": classifiers}} ) assert constructor(metadata) == helpers.dedent( @@ -1270,7 +1270,7 @@ def test_classifiers(self, constructor, isolation, helpers): def test_requires_python(self, constructor, isolation, helpers): metadata = ProjectMetadata( - str(isolation), None, {'project': {'name': 'My.App', 'version': '0.1.0', 'requires-python': '>=1,<2'}} + str(isolation), None, {"project": {"name": "My.App", "version": "0.1.0", "requires-python": ">=1,<2"}} ) assert constructor(metadata) == helpers.dedent( @@ -1286,7 +1286,7 @@ def test_dependencies(self, constructor, isolation, helpers): metadata = ProjectMetadata( str(isolation), None, - {'project': {'name': 'My.App', 'version': '0.1.0', 'dependencies': ['foo==1', 'bar==5']}}, + {"project": {"name": "My.App", "version": "0.1.0", "dependencies": ["foo==1", "bar==5"]}}, ) assert constructor(metadata) == helpers.dedent( @@ -1304,12 +1304,12 @@ def test_optional_dependencies(self, constructor, isolation, helpers): str(isolation), None, { - 'project': { - 'name': 'My.App', - 'version': '0.1.0', - 'optional-dependencies': { - 'feature2': ['foo==1; python_version < "3"', 'bar==5'], - 'feature1': ['foo==1', 'bar==5; python_version < "3"'], + "project": { + "name": "My.App", + "version": "0.1.0", + "optional-dependencies": { + "feature2": ['foo==1; python_version < "3"', "bar==5"], + "feature1": ["foo==1", 'bar==5; python_version < "3"'], }, } }, @@ -1334,12 +1334,12 @@ def test_optional_complex_dependencies(self, constructor, isolation, helpers): str(isolation), None, { - 'project': { - 'name': 'My.App', - 'version': '0.1.0', - 'optional-dependencies': { - 'feature2': ['foo==1; sys_platform == "win32" or python_version < "3"', 'bar==5'], - 'feature1': ['foo==1', 'bar==5; python_version < "3"'], + "project": { + "name": "My.App", + "version": "0.1.0", + "optional-dependencies": { + "feature2": ['foo==1; sys_platform == "win32" or python_version < "3"', "bar==5"], + "feature1": ["foo==1", 'bar==5; python_version < "3"'], }, } }, @@ -1363,10 +1363,10 @@ def test_extra_runtime_dependencies(self, constructor, isolation, helpers): metadata = ProjectMetadata( str(isolation), None, - {'project': {'name': 'My.App', 'version': '0.1.0', 'dependencies': ['foo==1', 'bar==5']}}, + {"project": {"name": "My.App", "version": "0.1.0", "dependencies": ["foo==1", "bar==5"]}}, ) - assert constructor(metadata, extra_dependencies=['baz==9']) == helpers.dedent( + assert constructor(metadata, extra_dependencies=["baz==9"]) == helpers.dedent( """ Metadata-Version: 2.2 Name: My.App @@ -1382,10 +1382,10 @@ def test_readme(self, constructor, isolation, helpers): str(isolation), None, { - 'project': { - 'name': 'My.App', - 'version': '0.1.0', - 'readme': {'content-type': 'text/markdown', 'text': 'test content\n'}, + "project": { + "name": "My.App", + "version": "0.1.0", + "readme": {"content-type": "text/markdown", "text": "test content\n"}, } }, ) @@ -1406,33 +1406,33 @@ def test_all(self, constructor, helpers, temp_dir): str(temp_dir), None, { - 'project': { - 'name': 'My.App', - 'version': '0.1.0', - 'description': 'foo', - 'urls': {'foo': 'bar', 'bar': 'baz'}, - 'authors': [{'email': 'bar@domain', 'name': 'foo'}], - 'maintainers': [{'email': 'bar@domain', 'name': 'foo'}], - 'license': {'text': 'foo\nbar'}, - 'keywords': ['foo', 'bar'], - 'classifiers': [ - 'Programming Language :: Python :: 3.11', - 'Programming Language :: Python :: 3.9', + "project": { + "name": "My.App", + "version": "0.1.0", + "description": "foo", + "urls": {"foo": "bar", "bar": "baz"}, + "authors": [{"email": "bar@domain", "name": "foo"}], + "maintainers": [{"email": "bar@domain", "name": "foo"}], + "license": {"text": "foo\nbar"}, + "keywords": ["foo", "bar"], + "classifiers": [ + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.9", ], - 'requires-python': '>=1,<2', - 'dependencies': ['foo==1', 'bar==5'], - 'optional-dependencies': { - 'feature2': ['foo==1; python_version < "3"', 'bar==5'], - 'feature1': ['foo==1', 'bar==5; python_version < "3"'], - 'feature3': ['baz @ file:///path/to/project'], + "requires-python": ">=1,<2", + "dependencies": ["foo==1", "bar==5"], + "optional-dependencies": { + "feature2": ['foo==1; python_version < "3"', "bar==5"], + "feature1": ["foo==1", 'bar==5; python_version < "3"'], + "feature3": ["baz @ file:///path/to/project"], }, - 'readme': {'content-type': 'text/markdown', 'text': 'test content\n'}, + "readme": {"content-type": "text/markdown", "text": "test content\n"}, }, - 'tool': {'hatch': {'metadata': {'allow-direct-references': True}}}, + "tool": {"hatch": {"metadata": {"allow-direct-references": True}}}, }, ) - (temp_dir / 'LICENSE.txt').touch() + (temp_dir / "LICENSE.txt").touch() assert constructor(metadata) == helpers.dedent( """ @@ -1467,10 +1467,10 @@ def test_all(self, constructor, helpers, temp_dir): ) -@pytest.mark.parametrize('constructor', [get_core_metadata_constructors()['2.3']]) +@pytest.mark.parametrize("constructor", [get_core_metadata_constructors()["2.3"]]) class TestCoreMetadataV23: def test_default(self, constructor, isolation, helpers): - metadata = ProjectMetadata(str(isolation), None, {'project': {'name': 'My.App', 'version': '0.1.0'}}) + metadata = ProjectMetadata(str(isolation), None, {"project": {"name": "My.App", "version": "0.1.0"}}) assert constructor(metadata) == helpers.dedent( """ @@ -1482,7 +1482,7 @@ def test_default(self, constructor, isolation, helpers): def test_description(self, constructor, isolation, helpers): metadata = ProjectMetadata( - str(isolation), None, {'project': {'name': 'My.App', 'version': '0.1.0', 'description': 'foo'}} + str(isolation), None, {"project": {"name": "My.App", "version": "0.1.0", "description": "foo"}} ) assert constructor(metadata) == helpers.dedent( @@ -1498,7 +1498,7 @@ def test_dynamic(self, constructor, isolation, helpers): metadata = ProjectMetadata( str(isolation), None, - {'project': {'name': 'My.App', 'version': '0.1.0', 'dynamic': ['authors', 'classifiers']}}, + {"project": {"name": "My.App", "version": "0.1.0", "dynamic": ["authors", "classifiers"]}}, ) assert constructor(metadata) == helpers.dedent( @@ -1516,7 +1516,7 @@ def test_urls(self, constructor, isolation, helpers): metadata = ProjectMetadata( str(isolation), None, - {'project': {'name': 'My.App', 'version': '0.1.0', 'urls': {'foo': 'bar', 'bar': 'baz'}}}, + {"project": {"name": "My.App", "version": "0.1.0", "urls": {"foo": "bar", "bar": "baz"}}}, ) assert constructor(metadata) == helpers.dedent( @@ -1531,7 +1531,7 @@ def test_urls(self, constructor, isolation, helpers): def test_authors_name(self, constructor, isolation, helpers): metadata = ProjectMetadata( - str(isolation), None, {'project': {'name': 'My.App', 'version': '0.1.0', 'authors': [{'name': 'foo'}]}} + str(isolation), None, {"project": {"name": "My.App", "version": "0.1.0", "authors": [{"name": "foo"}]}} ) assert constructor(metadata) == helpers.dedent( @@ -1547,7 +1547,7 @@ def test_authors_email(self, constructor, isolation, helpers): metadata = ProjectMetadata( str(isolation), None, - {'project': {'name': 'My.App', 'version': '0.1.0', 'authors': [{'email': 'foo@domain'}]}}, + {"project": {"name": "My.App", "version": "0.1.0", "authors": [{"email": "foo@domain"}]}}, ) assert constructor(metadata) == helpers.dedent( @@ -1563,7 +1563,7 @@ def test_authors_name_and_email(self, constructor, isolation, helpers): metadata = ProjectMetadata( str(isolation), None, - {'project': {'name': 'My.App', 'version': '0.1.0', 'authors': [{'email': 'bar@domain', 'name': 'foo'}]}}, + {"project": {"name": "My.App", "version": "0.1.0", "authors": [{"email": "bar@domain", "name": "foo"}]}}, ) assert constructor(metadata) == helpers.dedent( @@ -1579,7 +1579,7 @@ def test_authors_multiple(self, constructor, isolation, helpers): metadata = ProjectMetadata( str(isolation), None, - {'project': {'name': 'My.App', 'version': '0.1.0', 'authors': [{'name': 'foo'}, {'name': 'bar'}]}}, + {"project": {"name": "My.App", "version": "0.1.0", "authors": [{"name": "foo"}, {"name": "bar"}]}}, ) assert constructor(metadata) == helpers.dedent( @@ -1593,7 +1593,7 @@ def test_authors_multiple(self, constructor, isolation, helpers): def test_maintainers_name(self, constructor, isolation, helpers): metadata = ProjectMetadata( - str(isolation), None, {'project': {'name': 'My.App', 'version': '0.1.0', 'maintainers': [{'name': 'foo'}]}} + str(isolation), None, {"project": {"name": "My.App", "version": "0.1.0", "maintainers": [{"name": "foo"}]}} ) assert constructor(metadata) == helpers.dedent( @@ -1609,7 +1609,7 @@ def test_maintainers_email(self, constructor, isolation, helpers): metadata = ProjectMetadata( str(isolation), None, - {'project': {'name': 'My.App', 'version': '0.1.0', 'maintainers': [{'email': 'foo@domain'}]}}, + {"project": {"name": "My.App", "version": "0.1.0", "maintainers": [{"email": "foo@domain"}]}}, ) assert constructor(metadata) == helpers.dedent( @@ -1626,10 +1626,10 @@ def test_maintainers_name_and_email(self, constructor, isolation, helpers): str(isolation), None, { - 'project': { - 'name': 'My.App', - 'version': '0.1.0', - 'maintainers': [{'email': 'bar@domain', 'name': 'foo'}], + "project": { + "name": "My.App", + "version": "0.1.0", + "maintainers": [{"email": "bar@domain", "name": "foo"}], } }, ) @@ -1647,7 +1647,7 @@ def test_maintainers_multiple(self, constructor, isolation, helpers): metadata = ProjectMetadata( str(isolation), None, - {'project': {'name': 'My.App', 'version': '0.1.0', 'maintainers': [{'name': 'foo'}, {'name': 'bar'}]}}, + {"project": {"name": "My.App", "version": "0.1.0", "maintainers": [{"name": "foo"}, {"name": "bar"}]}}, ) assert constructor(metadata) == helpers.dedent( @@ -1661,7 +1661,7 @@ def test_maintainers_multiple(self, constructor, isolation, helpers): def test_license(self, constructor, isolation, helpers): metadata = ProjectMetadata( - str(isolation), None, {'project': {'name': 'My.App', 'version': '0.1.0', 'license': {'text': 'foo\nbar'}}} + str(isolation), None, {"project": {"name": "My.App", "version": "0.1.0", "license": {"text": "foo\nbar"}}} ) assert constructor(metadata) == helpers.dedent( @@ -1678,7 +1678,7 @@ def test_license_expression(self, constructor, isolation, helpers): metadata = ProjectMetadata( str(isolation), None, - {'project': {'name': 'My.App', 'version': '0.1.0', 'license': 'mit'}}, + {"project": {"name": "My.App", "version": "0.1.0", "license": "mit"}}, ) assert constructor(metadata) == helpers.dedent( @@ -1692,7 +1692,7 @@ def test_license_expression(self, constructor, isolation, helpers): def test_keywords_single(self, constructor, isolation, helpers): metadata = ProjectMetadata( - str(isolation), None, {'project': {'name': 'My.App', 'version': '0.1.0', 'keywords': ['foo']}} + str(isolation), None, {"project": {"name": "My.App", "version": "0.1.0", "keywords": ["foo"]}} ) assert constructor(metadata) == helpers.dedent( @@ -1706,7 +1706,7 @@ def test_keywords_single(self, constructor, isolation, helpers): def test_keywords_multiple(self, constructor, isolation, helpers): metadata = ProjectMetadata( - str(isolation), None, {'project': {'name': 'My.App', 'version': '0.1.0', 'keywords': ['foo', 'bar']}} + str(isolation), None, {"project": {"name": "My.App", "version": "0.1.0", "keywords": ["foo", "bar"]}} ) assert constructor(metadata) == helpers.dedent( @@ -1720,11 +1720,11 @@ def test_keywords_multiple(self, constructor, isolation, helpers): def test_classifiers(self, constructor, isolation, helpers): classifiers = [ - 'Programming Language :: Python :: 3.11', - 'Programming Language :: Python :: 3.9', + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.9", ] metadata = ProjectMetadata( - str(isolation), None, {'project': {'name': 'My.App', 'version': '0.1.0', 'classifiers': classifiers}} + str(isolation), None, {"project": {"name": "My.App", "version": "0.1.0", "classifiers": classifiers}} ) assert constructor(metadata) == helpers.dedent( @@ -1739,7 +1739,7 @@ def test_classifiers(self, constructor, isolation, helpers): def test_requires_python(self, constructor, isolation, helpers): metadata = ProjectMetadata( - str(isolation), None, {'project': {'name': 'My.App', 'version': '0.1.0', 'requires-python': '>=1,<2'}} + str(isolation), None, {"project": {"name": "My.App", "version": "0.1.0", "requires-python": ">=1,<2"}} ) assert constructor(metadata) == helpers.dedent( @@ -1755,7 +1755,7 @@ def test_dependencies(self, constructor, isolation, helpers): metadata = ProjectMetadata( str(isolation), None, - {'project': {'name': 'My.App', 'version': '0.1.0', 'dependencies': ['foo==1', 'bar==5']}}, + {"project": {"name": "My.App", "version": "0.1.0", "dependencies": ["foo==1", "bar==5"]}}, ) assert constructor(metadata) == helpers.dedent( @@ -1773,12 +1773,12 @@ def test_optional_dependencies(self, constructor, isolation, helpers): str(isolation), None, { - 'project': { - 'name': 'My.App', - 'version': '0.1.0', - 'optional-dependencies': { - 'feature2': ['foo==1; python_version < "3"', 'bar==5'], - 'feature1': ['foo==1', 'bar==5; python_version < "3"'], + "project": { + "name": "My.App", + "version": "0.1.0", + "optional-dependencies": { + "feature2": ['foo==1; python_version < "3"', "bar==5"], + "feature1": ["foo==1", 'bar==5; python_version < "3"'], }, } }, @@ -1802,10 +1802,10 @@ def test_extra_runtime_dependencies(self, constructor, isolation, helpers): metadata = ProjectMetadata( str(isolation), None, - {'project': {'name': 'My.App', 'version': '0.1.0', 'dependencies': ['foo==1', 'bar==5']}}, + {"project": {"name": "My.App", "version": "0.1.0", "dependencies": ["foo==1", "bar==5"]}}, ) - assert constructor(metadata, extra_dependencies=['baz==9']) == helpers.dedent( + assert constructor(metadata, extra_dependencies=["baz==9"]) == helpers.dedent( """ Metadata-Version: 2.3 Name: My.App @@ -1821,10 +1821,10 @@ def test_readme(self, constructor, isolation, helpers): str(isolation), None, { - 'project': { - 'name': 'My.App', - 'version': '0.1.0', - 'readme': {'content-type': 'text/markdown', 'text': 'test content\n'}, + "project": { + "name": "My.App", + "version": "0.1.0", + "readme": {"content-type": "text/markdown", "text": "test content\n"}, } }, ) @@ -1845,35 +1845,35 @@ def test_all(self, constructor, temp_dir, helpers): str(temp_dir), None, { - 'project': { - 'name': 'My.App', - 'version': '0.1.0', - 'description': 'foo', - 'urls': {'foo': 'bar', 'bar': 'baz'}, - 'authors': [{'email': 'bar@domain', 'name': 'foo'}], - 'maintainers': [{'email': 'bar@domain', 'name': 'foo'}], - 'keywords': ['foo', 'bar'], - 'classifiers': [ - 'Programming Language :: Python :: 3.11', - 'Programming Language :: Python :: 3.9', + "project": { + "name": "My.App", + "version": "0.1.0", + "description": "foo", + "urls": {"foo": "bar", "bar": "baz"}, + "authors": [{"email": "bar@domain", "name": "foo"}], + "maintainers": [{"email": "bar@domain", "name": "foo"}], + "keywords": ["foo", "bar"], + "classifiers": [ + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.9", ], - 'requires-python': '>=1,<2', - 'dependencies': ['foo==1', 'bar==5'], - 'optional-dependencies': { - 'feature2': ['foo==1; python_version < "3"', 'bar==5'], - 'feature1': ['foo==1', 'bar==5; python_version < "3"'], - 'feature3': ['baz @ file:///path/to/project'], + "requires-python": ">=1,<2", + "dependencies": ["foo==1", "bar==5"], + "optional-dependencies": { + "feature2": ['foo==1; python_version < "3"', "bar==5"], + "feature1": ["foo==1", 'bar==5; python_version < "3"'], + "feature3": ["baz @ file:///path/to/project"], }, - 'readme': {'content-type': 'text/markdown', 'text': 'test content\n'}, + "readme": {"content-type": "text/markdown", "text": "test content\n"}, }, - 'tool': {'hatch': {'metadata': {'allow-direct-references': True}}}, + "tool": {"hatch": {"metadata": {"allow-direct-references": True}}}, }, ) - licenses_dir = temp_dir / 'LICENSES' + licenses_dir = temp_dir / "LICENSES" licenses_dir.mkdir() - (licenses_dir / 'MIT.txt').touch() - (licenses_dir / 'Apache-2.0.txt').touch() + (licenses_dir / "MIT.txt").touch() + (licenses_dir / "Apache-2.0.txt").touch() assert constructor(metadata) == helpers.dedent( """ @@ -1906,10 +1906,10 @@ def test_all(self, constructor, temp_dir, helpers): ) -@pytest.mark.parametrize('constructor', [get_core_metadata_constructors()['2.4']]) +@pytest.mark.parametrize("constructor", [get_core_metadata_constructors()["2.4"]]) class TestCoreMetadataV24: def test_default(self, constructor, isolation, helpers): - metadata = ProjectMetadata(str(isolation), None, {'project': {'name': 'My.App', 'version': '0.1.0'}}) + metadata = ProjectMetadata(str(isolation), None, {"project": {"name": "My.App", "version": "0.1.0"}}) assert constructor(metadata) == helpers.dedent( """ @@ -1921,7 +1921,7 @@ def test_default(self, constructor, isolation, helpers): def test_description(self, constructor, isolation, helpers): metadata = ProjectMetadata( - str(isolation), None, {'project': {'name': 'My.App', 'version': '0.1.0', 'description': 'foo'}} + str(isolation), None, {"project": {"name": "My.App", "version": "0.1.0", "description": "foo"}} ) assert constructor(metadata) == helpers.dedent( @@ -1937,7 +1937,7 @@ def test_dynamic(self, constructor, isolation, helpers): metadata = ProjectMetadata( str(isolation), None, - {'project': {'name': 'My.App', 'version': '0.1.0', 'dynamic': ['authors', 'classifiers']}}, + {"project": {"name": "My.App", "version": "0.1.0", "dynamic": ["authors", "classifiers"]}}, ) assert constructor(metadata) == helpers.dedent( @@ -1955,7 +1955,7 @@ def test_urls(self, constructor, isolation, helpers): metadata = ProjectMetadata( str(isolation), None, - {'project': {'name': 'My.App', 'version': '0.1.0', 'urls': {'foo': 'bar', 'bar': 'baz'}}}, + {"project": {"name": "My.App", "version": "0.1.0", "urls": {"foo": "bar", "bar": "baz"}}}, ) assert constructor(metadata) == helpers.dedent( @@ -1970,7 +1970,7 @@ def test_urls(self, constructor, isolation, helpers): def test_authors_name(self, constructor, isolation, helpers): metadata = ProjectMetadata( - str(isolation), None, {'project': {'name': 'My.App', 'version': '0.1.0', 'authors': [{'name': 'foo'}]}} + str(isolation), None, {"project": {"name": "My.App", "version": "0.1.0", "authors": [{"name": "foo"}]}} ) assert constructor(metadata) == helpers.dedent( @@ -1986,7 +1986,7 @@ def test_authors_email(self, constructor, isolation, helpers): metadata = ProjectMetadata( str(isolation), None, - {'project': {'name': 'My.App', 'version': '0.1.0', 'authors': [{'email': 'foo@domain'}]}}, + {"project": {"name": "My.App", "version": "0.1.0", "authors": [{"email": "foo@domain"}]}}, ) assert constructor(metadata) == helpers.dedent( @@ -2002,7 +2002,7 @@ def test_authors_name_and_email(self, constructor, isolation, helpers): metadata = ProjectMetadata( str(isolation), None, - {'project': {'name': 'My.App', 'version': '0.1.0', 'authors': [{'email': 'bar@domain', 'name': 'foo'}]}}, + {"project": {"name": "My.App", "version": "0.1.0", "authors": [{"email": "bar@domain", "name": "foo"}]}}, ) assert constructor(metadata) == helpers.dedent( @@ -2018,7 +2018,7 @@ def test_authors_multiple(self, constructor, isolation, helpers): metadata = ProjectMetadata( str(isolation), None, - {'project': {'name': 'My.App', 'version': '0.1.0', 'authors': [{'name': 'foo'}, {'name': 'bar'}]}}, + {"project": {"name": "My.App", "version": "0.1.0", "authors": [{"name": "foo"}, {"name": "bar"}]}}, ) assert constructor(metadata) == helpers.dedent( @@ -2032,7 +2032,7 @@ def test_authors_multiple(self, constructor, isolation, helpers): def test_maintainers_name(self, constructor, isolation, helpers): metadata = ProjectMetadata( - str(isolation), None, {'project': {'name': 'My.App', 'version': '0.1.0', 'maintainers': [{'name': 'foo'}]}} + str(isolation), None, {"project": {"name": "My.App", "version": "0.1.0", "maintainers": [{"name": "foo"}]}} ) assert constructor(metadata) == helpers.dedent( @@ -2048,7 +2048,7 @@ def test_maintainers_email(self, constructor, isolation, helpers): metadata = ProjectMetadata( str(isolation), None, - {'project': {'name': 'My.App', 'version': '0.1.0', 'maintainers': [{'email': 'foo@domain'}]}}, + {"project": {"name": "My.App", "version": "0.1.0", "maintainers": [{"email": "foo@domain"}]}}, ) assert constructor(metadata) == helpers.dedent( @@ -2065,10 +2065,10 @@ def test_maintainers_name_and_email(self, constructor, isolation, helpers): str(isolation), None, { - 'project': { - 'name': 'My.App', - 'version': '0.1.0', - 'maintainers': [{'email': 'bar@domain', 'name': 'foo'}], + "project": { + "name": "My.App", + "version": "0.1.0", + "maintainers": [{"email": "bar@domain", "name": "foo"}], } }, ) @@ -2086,7 +2086,7 @@ def test_maintainers_multiple(self, constructor, isolation, helpers): metadata = ProjectMetadata( str(isolation), None, - {'project': {'name': 'My.App', 'version': '0.1.0', 'maintainers': [{'name': 'foo'}, {'name': 'bar'}]}}, + {"project": {"name": "My.App", "version": "0.1.0", "maintainers": [{"name": "foo"}, {"name": "bar"}]}}, ) assert constructor(metadata) == helpers.dedent( @@ -2100,7 +2100,7 @@ def test_maintainers_multiple(self, constructor, isolation, helpers): def test_license(self, constructor, isolation, helpers): metadata = ProjectMetadata( - str(isolation), None, {'project': {'name': 'My.App', 'version': '0.1.0', 'license': {'text': 'foo\nbar'}}} + str(isolation), None, {"project": {"name": "My.App", "version": "0.1.0", "license": {"text": "foo\nbar"}}} ) assert constructor(metadata) == helpers.dedent( @@ -2117,7 +2117,7 @@ def test_license_expression(self, constructor, isolation, helpers): metadata = ProjectMetadata( str(isolation), None, - {'project': {'name': 'My.App', 'version': '0.1.0', 'license': 'mit or apache-2.0'}}, + {"project": {"name": "My.App", "version": "0.1.0", "license": "mit or apache-2.0"}}, ) assert constructor(metadata) == helpers.dedent( @@ -2133,13 +2133,13 @@ def test_license_files(self, constructor, temp_dir, helpers): metadata = ProjectMetadata( str(temp_dir), None, - {'project': {'name': 'My.App', 'version': '0.1.0', 'license-files': ['LICENSES/*']}}, + {"project": {"name": "My.App", "version": "0.1.0", "license-files": ["LICENSES/*"]}}, ) - licenses_dir = temp_dir / 'LICENSES' + licenses_dir = temp_dir / "LICENSES" licenses_dir.mkdir() - (licenses_dir / 'MIT.txt').touch() - (licenses_dir / 'Apache-2.0.txt').touch() + (licenses_dir / "MIT.txt").touch() + (licenses_dir / "Apache-2.0.txt").touch() assert constructor(metadata) == helpers.dedent( """ @@ -2153,7 +2153,7 @@ def test_license_files(self, constructor, temp_dir, helpers): def test_keywords_single(self, constructor, isolation, helpers): metadata = ProjectMetadata( - str(isolation), None, {'project': {'name': 'My.App', 'version': '0.1.0', 'keywords': ['foo']}} + str(isolation), None, {"project": {"name": "My.App", "version": "0.1.0", "keywords": ["foo"]}} ) assert constructor(metadata) == helpers.dedent( @@ -2167,7 +2167,7 @@ def test_keywords_single(self, constructor, isolation, helpers): def test_keywords_multiple(self, constructor, isolation, helpers): metadata = ProjectMetadata( - str(isolation), None, {'project': {'name': 'My.App', 'version': '0.1.0', 'keywords': ['foo', 'bar']}} + str(isolation), None, {"project": {"name": "My.App", "version": "0.1.0", "keywords": ["foo", "bar"]}} ) assert constructor(metadata) == helpers.dedent( @@ -2181,11 +2181,11 @@ def test_keywords_multiple(self, constructor, isolation, helpers): def test_classifiers(self, constructor, isolation, helpers): classifiers = [ - 'Programming Language :: Python :: 3.11', - 'Programming Language :: Python :: 3.9', + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.9", ] metadata = ProjectMetadata( - str(isolation), None, {'project': {'name': 'My.App', 'version': '0.1.0', 'classifiers': classifiers}} + str(isolation), None, {"project": {"name": "My.App", "version": "0.1.0", "classifiers": classifiers}} ) assert constructor(metadata) == helpers.dedent( @@ -2200,7 +2200,7 @@ def test_classifiers(self, constructor, isolation, helpers): def test_requires_python(self, constructor, isolation, helpers): metadata = ProjectMetadata( - str(isolation), None, {'project': {'name': 'My.App', 'version': '0.1.0', 'requires-python': '>=1,<2'}} + str(isolation), None, {"project": {"name": "My.App", "version": "0.1.0", "requires-python": ">=1,<2"}} ) assert constructor(metadata) == helpers.dedent( @@ -2216,7 +2216,7 @@ def test_dependencies(self, constructor, isolation, helpers): metadata = ProjectMetadata( str(isolation), None, - {'project': {'name': 'My.App', 'version': '0.1.0', 'dependencies': ['foo==1', 'bar==5']}}, + {"project": {"name": "My.App", "version": "0.1.0", "dependencies": ["foo==1", "bar==5"]}}, ) assert constructor(metadata) == helpers.dedent( @@ -2234,12 +2234,12 @@ def test_optional_dependencies(self, constructor, isolation, helpers): str(isolation), None, { - 'project': { - 'name': 'My.App', - 'version': '0.1.0', - 'optional-dependencies': { - 'feature2': ['foo==1; python_version < "3"', 'bar==5'], - 'feature1': ['foo==1', 'bar==5; python_version < "3"'], + "project": { + "name": "My.App", + "version": "0.1.0", + "optional-dependencies": { + "feature2": ['foo==1; python_version < "3"', "bar==5"], + "feature1": ["foo==1", 'bar==5; python_version < "3"'], }, } }, @@ -2263,10 +2263,10 @@ def test_extra_runtime_dependencies(self, constructor, isolation, helpers): metadata = ProjectMetadata( str(isolation), None, - {'project': {'name': 'My.App', 'version': '0.1.0', 'dependencies': ['foo==1', 'bar==5']}}, + {"project": {"name": "My.App", "version": "0.1.0", "dependencies": ["foo==1", "bar==5"]}}, ) - assert constructor(metadata, extra_dependencies=['baz==9']) == helpers.dedent( + assert constructor(metadata, extra_dependencies=["baz==9"]) == helpers.dedent( """ Metadata-Version: 2.4 Name: My.App @@ -2282,10 +2282,10 @@ def test_readme(self, constructor, isolation, helpers): str(isolation), None, { - 'project': { - 'name': 'My.App', - 'version': '0.1.0', - 'readme': {'content-type': 'text/markdown', 'text': 'test content\n'}, + "project": { + "name": "My.App", + "version": "0.1.0", + "readme": {"content-type": "text/markdown", "text": "test content\n"}, } }, ) @@ -2306,37 +2306,37 @@ def test_all(self, constructor, temp_dir, helpers): str(temp_dir), None, { - 'project': { - 'name': 'My.App', - 'version': '0.1.0', - 'description': 'foo', - 'urls': {'foo': 'bar', 'bar': 'baz'}, - 'authors': [{'email': 'bar@domain', 'name': 'foo'}], - 'maintainers': [{'email': 'bar@domain', 'name': 'foo'}], - 'license': 'mit or apache-2.0', - 'license-files': ['LICENSES/*'], - 'keywords': ['foo', 'bar'], - 'classifiers': [ - 'Programming Language :: Python :: 3.11', - 'Programming Language :: Python :: 3.9', + "project": { + "name": "My.App", + "version": "0.1.0", + "description": "foo", + "urls": {"foo": "bar", "bar": "baz"}, + "authors": [{"email": "bar@domain", "name": "foo"}], + "maintainers": [{"email": "bar@domain", "name": "foo"}], + "license": "mit or apache-2.0", + "license-files": ["LICENSES/*"], + "keywords": ["foo", "bar"], + "classifiers": [ + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.9", ], - 'requires-python': '>=1,<2', - 'dependencies': ['foo==1', 'bar==5'], - 'optional-dependencies': { - 'feature2': ['foo==1; python_version < "3"', 'bar==5'], - 'feature1': ['foo==1', 'bar==5; python_version < "3"'], - 'feature3': ['baz @ file:///path/to/project'], + "requires-python": ">=1,<2", + "dependencies": ["foo==1", "bar==5"], + "optional-dependencies": { + "feature2": ['foo==1; python_version < "3"', "bar==5"], + "feature1": ["foo==1", 'bar==5; python_version < "3"'], + "feature3": ["baz @ file:///path/to/project"], }, - 'readme': {'content-type': 'text/markdown', 'text': 'test content\n'}, + "readme": {"content-type": "text/markdown", "text": "test content\n"}, }, - 'tool': {'hatch': {'metadata': {'allow-direct-references': True}}}, + "tool": {"hatch": {"metadata": {"allow-direct-references": True}}}, }, ) - licenses_dir = temp_dir / 'LICENSES' + licenses_dir = temp_dir / "LICENSES" licenses_dir.mkdir() - (licenses_dir / 'MIT.txt').touch() - (licenses_dir / 'Apache-2.0.txt').touch() + (licenses_dir / "MIT.txt").touch() + (licenses_dir / "Apache-2.0.txt").touch() assert constructor(metadata) == helpers.dedent( """ diff --git a/tests/backend/test_build.py b/tests/backend/test_build.py index 810956dab..6b4f86599 100644 --- a/tests/backend/test_build.py +++ b/tests/backend/test_build.py @@ -2,18 +2,18 @@ def test_sdist(hatch, helpers, temp_dir, config_file): - config_file.model.template.plugins['default']['src-layout'] = False + config_file.model.template.plugins["default"]["src-layout"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - project_config = project_path / 'pyproject.toml' + project_path = temp_dir / "my-app" + project_config = project_path / "pyproject.toml" project_config.write_text( helpers.dedent( """ @@ -30,7 +30,7 @@ def test_sdist(hatch, helpers, temp_dir, config_file): ) ) - build_path = project_path / 'dist' + build_path = project_path / "dist" build_path.mkdir() with project_path.as_cwd(): @@ -39,22 +39,22 @@ def test_sdist(hatch, helpers, temp_dir, config_file): build_artifacts = list(build_path.iterdir()) assert len(build_artifacts) == 1 assert expected_artifact == str(build_artifacts[0].name) - assert expected_artifact.endswith('.tar.gz') + assert expected_artifact.endswith(".tar.gz") def test_wheel(hatch, helpers, temp_dir, config_file): - config_file.model.template.plugins['default']['src-layout'] = False + config_file.model.template.plugins["default"]["src-layout"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - project_config = project_path / 'pyproject.toml' + project_path = temp_dir / "my-app" + project_config = project_path / "pyproject.toml" project_config.write_text( helpers.dedent( """ @@ -71,7 +71,7 @@ def test_wheel(hatch, helpers, temp_dir, config_file): ) ) - build_path = project_path / 'dist' + build_path = project_path / "dist" build_path.mkdir() with project_path.as_cwd(): @@ -80,22 +80,22 @@ def test_wheel(hatch, helpers, temp_dir, config_file): build_artifacts = list(build_path.iterdir()) assert len(build_artifacts) == 1 assert expected_artifact == str(build_artifacts[0].name) - assert expected_artifact.endswith('.whl') + assert expected_artifact.endswith(".whl") def test_editable(hatch, helpers, temp_dir, config_file): - config_file.model.template.plugins['default']['src-layout'] = False + config_file.model.template.plugins["default"]["src-layout"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - project_config = project_path / 'pyproject.toml' + project_path = temp_dir / "my-app" + project_config = project_path / "pyproject.toml" project_config.write_text( helpers.dedent( """ @@ -112,7 +112,7 @@ def test_editable(hatch, helpers, temp_dir, config_file): ) ) - build_path = project_path / 'dist' + build_path = project_path / "dist" build_path.mkdir() with project_path.as_cwd(): @@ -121,4 +121,4 @@ def test_editable(hatch, helpers, temp_dir, config_file): build_artifacts = list(build_path.iterdir()) assert len(build_artifacts) == 1 assert expected_artifact == str(build_artifacts[0].name) - assert expected_artifact.endswith('.whl') + assert expected_artifact.endswith(".whl") diff --git a/tests/backend/utils/test_context.py b/tests/backend/utils/test_context.py index 65d43f864..90f722b17 100644 --- a/tests/backend/utils/test_context.py +++ b/tests/backend/utils/test_context.py @@ -8,134 +8,134 @@ def test_normal(isolation): context = Context(isolation) - assert context.format('foo {0} {key}', 'arg', key='value') == 'foo arg value' + assert context.format("foo {0} {key}", "arg", key="value") == "foo arg value" class TestStatic: def test_directory_separator(self, isolation): context = Context(isolation) - assert context.format('foo {/}') == f'foo {os.sep}' + assert context.format("foo {/}") == f"foo {os.sep}" def test_path_separator(self, isolation): context = Context(isolation) - assert context.format('foo {;}') == f'foo {os.pathsep}' + assert context.format("foo {;}") == f"foo {os.pathsep}" class TestRoot: def test_default(self, isolation): context = Context(isolation) - assert context.format('foo {root}') == f'foo {isolation}' + assert context.format("foo {root}") == f"foo {isolation}" def test_parent(self, isolation): context = Context(isolation) path = os.path.dirname(str(isolation)) - assert context.format('foo {root:parent}') == f'foo {path}' + assert context.format("foo {root:parent}") == f"foo {path}" def test_parent_parent(self, isolation): context = Context(isolation) path = os.path.dirname(os.path.dirname(str(isolation))) - assert context.format('foo {root:parent:parent}') == f'foo {path}' + assert context.format("foo {root:parent:parent}") == f"foo {path}" def test_uri(self, isolation, uri_slash_prefix): context = Context(isolation) - normalized_path = str(isolation).replace(os.sep, '/') - assert context.format('foo {root:uri}') == f'foo file:{uri_slash_prefix}{normalized_path}' + normalized_path = str(isolation).replace(os.sep, "/") + assert context.format("foo {root:uri}") == f"foo file:{uri_slash_prefix}{normalized_path}" def test_uri_parent(self, isolation, uri_slash_prefix): context = Context(isolation) - normalized_path = os.path.dirname(str(isolation)).replace(os.sep, '/') - assert context.format('foo {root:parent:uri}') == f'foo file:{uri_slash_prefix}{normalized_path}' + normalized_path = os.path.dirname(str(isolation)).replace(os.sep, "/") + assert context.format("foo {root:parent:uri}") == f"foo file:{uri_slash_prefix}{normalized_path}" def test_uri_parent_parent(self, isolation, uri_slash_prefix): context = Context(isolation) - normalized_path = os.path.dirname(os.path.dirname(str(isolation))).replace(os.sep, '/') - assert context.format('foo {root:parent:parent:uri}') == f'foo file:{uri_slash_prefix}{normalized_path}' + normalized_path = os.path.dirname(os.path.dirname(str(isolation))).replace(os.sep, "/") + assert context.format("foo {root:parent:parent:uri}") == f"foo file:{uri_slash_prefix}{normalized_path}" def test_real(self, isolation): context = Context(isolation) real_path = os.path.realpath(isolation) - assert context.format('foo {root:real}') == f'foo {real_path}' + assert context.format("foo {root:real}") == f"foo {real_path}" def test_real_parent(self, isolation): context = Context(isolation) real_path = os.path.dirname(os.path.realpath(isolation)) - assert context.format('foo {root:parent:real}') == f'foo {real_path}' + assert context.format("foo {root:parent:real}") == f"foo {real_path}" def test_real_parent_parent(self, isolation): context = Context(isolation) real_path = os.path.dirname(os.path.dirname(os.path.realpath(isolation))) - assert context.format('foo {root:parent:parent:real}') == f'foo {real_path}' + assert context.format("foo {root:parent:parent:real}") == f"foo {real_path}" def test_unknown_modifier(self, isolation): context = Context(isolation) - with pytest.raises(ValueError, match='Unknown path modifier: bar'): - context.format('foo {root:bar}') + with pytest.raises(ValueError, match="Unknown path modifier: bar"): + context.format("foo {root:bar}") def test_too_many_modifiers_after_parent(self, isolation): context = Context(isolation) - with pytest.raises(ValueError, match='Expected a single path modifier and instead got: foo, bar, baz'): - context.format('foo {root:parent:foo:bar:baz}') + with pytest.raises(ValueError, match="Expected a single path modifier and instead got: foo, bar, baz"): + context.format("foo {root:parent:foo:bar:baz}") class TestHome: def test_default(self, isolation): context = Context(isolation) - assert context.format('foo {home}') == f'foo {os.path.expanduser("~")}' + assert context.format("foo {home}") == f"foo {os.path.expanduser('~')}" def test_uri(self, isolation, uri_slash_prefix): context = Context(isolation) - normalized_path = os.path.expanduser('~').replace(os.sep, '/') - assert context.format('foo {home:uri}') == f'foo file:{uri_slash_prefix}{normalized_path}' + normalized_path = os.path.expanduser("~").replace(os.sep, "/") + assert context.format("foo {home:uri}") == f"foo file:{uri_slash_prefix}{normalized_path}" def test_real(self, isolation): context = Context(isolation) - assert context.format('foo {home:real}') == f'foo {os.path.realpath(os.path.expanduser("~"))}' + assert context.format("foo {home:real}") == f"foo {os.path.realpath(os.path.expanduser('~'))}" def test_unknown_modifier(self, isolation): context = Context(isolation) - with pytest.raises(ValueError, match='Unknown path modifier: bar'): - context.format('foo {home:bar}') + with pytest.raises(ValueError, match="Unknown path modifier: bar"): + context.format("foo {home:bar}") class TestEnvVars: def test_set(self, isolation): context = Context(isolation) - with EnvVars({'BAR': 'foobarbaz'}): - assert context.format('foo {env:BAR}') == 'foo foobarbaz' + with EnvVars({"BAR": "foobarbaz"}): + assert context.format("foo {env:BAR}") == "foo foobarbaz" def test_default(self, isolation): context = Context(isolation) - assert context.format('foo {env:BAR:foobarbaz}') == 'foo foobarbaz' + assert context.format("foo {env:BAR:foobarbaz}") == "foo foobarbaz" def test_default_empty_string(self, isolation): context = Context(isolation) - assert context.format('foo {env:BAR:}') == 'foo ' + assert context.format("foo {env:BAR:}") == "foo " def test_default_nested_set(self, isolation): context = Context(isolation) - with EnvVars({'BAZ': 'foobarbaz'}): - assert context.format('foo {env:BAR:{env:BAZ}}') == 'foo foobarbaz' + with EnvVars({"BAZ": "foobarbaz"}): + assert context.format("foo {env:BAR:{env:BAZ}}") == "foo foobarbaz" def test_default_nested_default(self, isolation): context = Context(isolation) - assert context.format('foo {env:BAR:{env:BAZ:{home}}}') == f'foo {os.path.expanduser("~")}' + assert context.format("foo {env:BAR:{env:BAZ:{home}}}") == f"foo {os.path.expanduser('~')}" def test_no_selection(self, isolation): context = Context(isolation) - with pytest.raises(ValueError, match='The `env` context formatting field requires a modifier'): - context.format('foo {env}') + with pytest.raises(ValueError, match="The `env` context formatting field requires a modifier"): + context.format("foo {env}") def test_unset_without_default(self, isolation): context = Context(isolation) - with pytest.raises(ValueError, match='Nonexistent environment variable must set a default: BAR'): - context.format('foo {env:BAR}') + with pytest.raises(ValueError, match="Nonexistent environment variable must set a default: BAR"): + context.format("foo {env:BAR}") diff --git a/tests/backend/utils/test_fs.py b/tests/backend/utils/test_fs.py index 27894b97f..1c2b20109 100644 --- a/tests/backend/utils/test_fs.py +++ b/tests/backend/utils/test_fs.py @@ -5,11 +5,11 @@ class TestPathToURI: def test_unix(self, isolation, uri_slash_prefix): - bad_path = f'{isolation}{os.sep}' - normalized_path = str(isolation).replace(os.sep, '/') - assert path_to_uri(bad_path) == f'file:{uri_slash_prefix}{normalized_path}' + bad_path = f"{isolation}{os.sep}" + normalized_path = str(isolation).replace(os.sep, "/") + assert path_to_uri(bad_path) == f"file:{uri_slash_prefix}{normalized_path}" def test_character_escaping(self, temp_dir, uri_slash_prefix): - path = temp_dir / 'foo bar' - normalized_path = str(path).replace(os.sep, '/').replace(' ', '%20') - assert path_to_uri(path) == f'file:{uri_slash_prefix}{normalized_path}' + path = temp_dir / "foo bar" + normalized_path = str(path).replace(os.sep, "/").replace(" ", "%20") + assert path_to_uri(path) == f"file:{uri_slash_prefix}{normalized_path}" diff --git a/tests/backend/utils/test_macos.py b/tests/backend/utils/test_macos.py index 3716ec556..b95a708d9 100644 --- a/tests/backend/utils/test_macos.py +++ b/tests/backend/utils/test_macos.py @@ -8,18 +8,18 @@ @pytest.mark.parametrize( - ('plat', 'arch', 'compat', 'archflags', 'deptarget', 'expected'), + ("plat", "arch", "compat", "archflags", "deptarget", "expected"), [ - ('macosx_10_9_x86_64', 'x86_64', False, '', '', 'macosx_10_9_x86_64'), - ('macosx_11_9_x86_64', 'x86_64', False, '', '', 'macosx_11_0_x86_64'), - ('macosx_12_0_x86_64', 'x86_64', True, '', '', 'macosx_10_16_x86_64'), - ('macosx_10_9_arm64', 'arm64', False, '', '', 'macosx_11_0_arm64'), - ('macosx_10_9_arm64', 'arm64', False, '-arch x86_64 -arch arm64', '', 'macosx_10_9_universal2'), - ('macosx_10_9_x86_64', 'x86_64', False, '-arch x86_64 -arch arm64', '', 'macosx_10_9_universal2'), - ('macosx_10_9_x86_64', 'x86_64', False, '-arch x86_64 -arch arm64', '12', 'macosx_12_0_universal2'), - ('macosx_10_9_x86_64', 'x86_64', False, '-arch arm64', '12.4', 'macosx_12_0_arm64'), - ('macosx_10_9_x86_64', 'x86_64', False, '-arch arm64', '10.12', 'macosx_11_0_arm64'), - ('macosx_10_9_x86_64', 'x86_64', True, '-arch arm64', '10.12', 'macosx_10_16_arm64'), + ("macosx_10_9_x86_64", "x86_64", False, "", "", "macosx_10_9_x86_64"), + ("macosx_11_9_x86_64", "x86_64", False, "", "", "macosx_11_0_x86_64"), + ("macosx_12_0_x86_64", "x86_64", True, "", "", "macosx_10_16_x86_64"), + ("macosx_10_9_arm64", "arm64", False, "", "", "macosx_11_0_arm64"), + ("macosx_10_9_arm64", "arm64", False, "-arch x86_64 -arch arm64", "", "macosx_10_9_universal2"), + ("macosx_10_9_x86_64", "x86_64", False, "-arch x86_64 -arch arm64", "", "macosx_10_9_universal2"), + ("macosx_10_9_x86_64", "x86_64", False, "-arch x86_64 -arch arm64", "12", "macosx_12_0_universal2"), + ("macosx_10_9_x86_64", "x86_64", False, "-arch arm64", "12.4", "macosx_12_0_arm64"), + ("macosx_10_9_x86_64", "x86_64", False, "-arch arm64", "10.12", "macosx_11_0_arm64"), + ("macosx_10_9_x86_64", "x86_64", True, "-arch arm64", "10.12", "macosx_10_16_arm64"), ], ) def test_process_macos_plat_tag( @@ -32,24 +32,24 @@ def test_process_macos_plat_tag( deptarget: str, expected: str, ) -> None: - monkeypatch.setenv('ARCHFLAGS', archflags) - monkeypatch.setenv('MACOSX_DEPLOYMENT_TARGET', deptarget) - monkeypatch.setattr(platform, 'machine', lambda: arch) + monkeypatch.setenv("ARCHFLAGS", archflags) + monkeypatch.setenv("MACOSX_DEPLOYMENT_TARGET", deptarget) + monkeypatch.setattr(platform, "machine", lambda: arch) assert process_macos_plat_tag(plat, compat=compat) == expected @pytest.mark.parametrize( - ('version', 'arm', 'compat', 'expected'), + ("version", "arm", "compat", "expected"), [ - ('10_9', False, False, '10_9'), - ('10_9', False, True, '10_9'), - ('10_9', True, False, '11_0'), - ('10_9', True, True, '10_9'), - ('11_3', False, False, '11_0'), - ('12_3', True, False, '12_0'), - ('12_3', False, True, '10_16'), - ('12_3', True, True, '10_16'), + ("10_9", False, False, "10_9"), + ("10_9", False, True, "10_9"), + ("10_9", True, False, "11_0"), + ("10_9", True, True, "10_9"), + ("11_3", False, False, "11_0"), + ("12_3", True, False, "12_0"), + ("12_3", False, True, "10_16"), + ("12_3", True, True, "10_16"), ], ) def check_normalization(*, version: str, arm: bool, compat: bool, expected: str) -> None: diff --git a/tests/backend/version/scheme/test_standard.py b/tests/backend/version/scheme/test_standard.py index fc58123ce..924806ab5 100644 --- a/tests/backend/version/scheme/test_standard.py +++ b/tests/backend/version/scheme/test_standard.py @@ -9,140 +9,140 @@ def test_not_higher(isolation): scheme = StandardScheme(str(isolation), {}) - with pytest.raises(ValueError, match='Version `1.0.0` is not higher than the original version `1.0`'): - scheme.update('1.0.0', '1.0', {}) + with pytest.raises(ValueError, match="Version `1.0.0` is not higher than the original version `1.0`"): + scheme.update("1.0.0", "1.0", {}) def test_specific(isolation): scheme = StandardScheme(str(isolation), {}) - assert scheme.update('9000.0.0-rc.1', '1.0', {}) == '9000.0.0rc1' + assert scheme.update("9000.0.0-rc.1", "1.0", {}) == "9000.0.0rc1" def test_specific_not_higher_allowed_config(isolation): - scheme = StandardScheme(str(isolation), {'validate-bump': False}) + scheme = StandardScheme(str(isolation), {"validate-bump": False}) - assert scheme.update('0.24.4', '1.0.0.dev0', {}) == '0.24.4' + assert scheme.update("0.24.4", "1.0.0.dev0", {}) == "0.24.4" def test_specific_not_higher_allowed_env_var(isolation): scheme = StandardScheme(str(isolation), {}) - with EnvVars({VersionEnvVars.VALIDATE_BUMP: 'false'}): - assert scheme.update('0.24.4', '1.0.0.dev0', {}) == '0.24.4' + with EnvVars({VersionEnvVars.VALIDATE_BUMP: "false"}): + assert scheme.update("0.24.4", "1.0.0.dev0", {}) == "0.24.4" def test_release(isolation): scheme = StandardScheme(str(isolation), {}) - assert scheme.update('release', '9000.0.0-rc.1.post7.dev5', {}) == '9000.0.0' + assert scheme.update("release", "9000.0.0-rc.1.post7.dev5", {}) == "9000.0.0" def test_major(isolation): scheme = StandardScheme(str(isolation), {}) - assert scheme.update('major', '9000.0.0-rc.1', {}) == '9001.0.0' + assert scheme.update("major", "9000.0.0-rc.1", {}) == "9001.0.0" def test_minor(isolation): scheme = StandardScheme(str(isolation), {}) - assert scheme.update('minor', '9000.0.0-rc.1', {}) == '9000.1.0' + assert scheme.update("minor", "9000.0.0-rc.1", {}) == "9000.1.0" -@pytest.mark.parametrize('keyword', ['micro', 'patch', 'fix']) +@pytest.mark.parametrize("keyword", ["micro", "patch", "fix"]) def test_micro(isolation, keyword): scheme = StandardScheme(str(isolation), {}) - assert scheme.update(keyword, '9000.0.0-rc.1', {}) == '9000.0.1' + assert scheme.update(keyword, "9000.0.0-rc.1", {}) == "9000.0.1" class TestPre: - @pytest.mark.parametrize('phase', ['a', 'b', 'c', 'rc', 'alpha', 'beta', 'pre', 'preview']) + @pytest.mark.parametrize("phase", ["a", "b", "c", "rc", "alpha", "beta", "pre", "preview"]) def test_begin(self, isolation, phase): scheme = StandardScheme(str(isolation), {}) normalized_phase, _ = _parse_letter_version(phase, 0) - assert scheme.update(phase, '9000.0.0.post7.dev5', {}) == f'9000.0.0{normalized_phase}0' + assert scheme.update(phase, "9000.0.0.post7.dev5", {}) == f"9000.0.0{normalized_phase}0" - @pytest.mark.parametrize('phase', ['a', 'b', 'c', 'rc', 'alpha', 'beta', 'pre', 'preview']) + @pytest.mark.parametrize("phase", ["a", "b", "c", "rc", "alpha", "beta", "pre", "preview"]) def test_continue(self, isolation, phase): scheme = StandardScheme(str(isolation), {}) normalized_phase, _ = _parse_letter_version(phase, 0) - assert scheme.update(phase, f'9000.0.0{phase}0.post7.dev5', {}) == f'9000.0.0{normalized_phase}1' + assert scheme.update(phase, f"9000.0.0{phase}0.post7.dev5", {}) == f"9000.0.0{normalized_phase}1" - @pytest.mark.parametrize('phase', ['a', 'b', 'c', 'rc', 'alpha', 'beta', 'pre', 'preview']) + @pytest.mark.parametrize("phase", ["a", "b", "c", "rc", "alpha", "beta", "pre", "preview"]) def test_restart(self, isolation, phase): scheme = StandardScheme(str(isolation), {}) normalized_phase, _ = _parse_letter_version(phase, 0) - other_phase = 'b' if normalized_phase == 'a' else 'a' - assert scheme.update(phase, f'9000.0.0-{other_phase}5.post7.dev5', {}) == f'9000.0.0{normalized_phase}0' + other_phase = "b" if normalized_phase == "a" else "a" + assert scheme.update(phase, f"9000.0.0-{other_phase}5.post7.dev5", {}) == f"9000.0.0{normalized_phase}0" class TestPost: - @pytest.mark.parametrize('key', ['post', 'rev', 'r']) + @pytest.mark.parametrize("key", ["post", "rev", "r"]) def test_begin(self, isolation, key): scheme = StandardScheme(str(isolation), {}) - assert scheme.update(key, '9000.0.0-rc.3.dev5', {}) == '9000.0.0rc3.post0' + assert scheme.update(key, "9000.0.0-rc.3.dev5", {}) == "9000.0.0rc3.post0" - @pytest.mark.parametrize('key', ['post', 'rev', 'r']) + @pytest.mark.parametrize("key", ["post", "rev", "r"]) def test_continue(self, isolation, key): scheme = StandardScheme(str(isolation), {}) - assert scheme.update(key, f'9000.0.0-rc.3-{key}7.dev5', {}) == '9000.0.0rc3.post8' + assert scheme.update(key, f"9000.0.0-rc.3-{key}7.dev5", {}) == "9000.0.0rc3.post8" class TestDev: def test_begin(self, isolation): scheme = StandardScheme(str(isolation), {}) - assert scheme.update('dev', '9000.0.0-rc.3-7', {}) == '9000.0.0rc3.post7.dev0' + assert scheme.update("dev", "9000.0.0-rc.3-7", {}) == "9000.0.0rc3.post7.dev0" def test_continue(self, isolation): scheme = StandardScheme(str(isolation), {}) - assert scheme.update('dev', '9000.0.0-rc.3-7.dev5', {}) == '9000.0.0rc3.post7.dev6' + assert scheme.update("dev", "9000.0.0-rc.3-7.dev5", {}) == "9000.0.0rc3.post7.dev6" class TestMultiple: def test_explicit_error(self, isolation): scheme = StandardScheme(str(isolation), {}) - with pytest.raises(ValueError, match='Cannot specify multiple update operations with an explicit version'): - scheme.update('5,rc', '3', {}) + with pytest.raises(ValueError, match="Cannot specify multiple update operations with an explicit version"): + scheme.update("5,rc", "3", {}) @pytest.mark.parametrize( - ('operations', 'expected'), + ("operations", "expected"), [ - ('fix,rc', '0.0.2rc0'), - ('minor,dev', '0.1.0.dev0'), - ('minor,preview', '0.1.0rc0'), - ('major,beta', '1.0.0b0'), - ('major,major,major', '3.0.0'), + ("fix,rc", "0.0.2rc0"), + ("minor,dev", "0.1.0.dev0"), + ("minor,preview", "0.1.0rc0"), + ("major,beta", "1.0.0b0"), + ("major,major,major", "3.0.0"), ], ) def test_correct(self, isolation, operations, expected): scheme = StandardScheme(str(isolation), {}) - assert scheme.update(operations, '0.0.1', {}) == expected + assert scheme.update(operations, "0.0.1", {}) == expected class TestWithEpoch: @pytest.mark.parametrize( - ('operations', 'expected'), + ("operations", "expected"), [ - ('patch,dev,release', '1!0.0.2'), - ('fix,rc', '1!0.0.2rc0'), - ('minor,dev', '1!0.1.0.dev0'), - ('minor,preview', '1!0.1.0rc0'), - ('major,beta', '1!1.0.0b0'), - ('major,major,major', '1!3.0.0'), + ("patch,dev,release", "1!0.0.2"), + ("fix,rc", "1!0.0.2rc0"), + ("minor,dev", "1!0.1.0.dev0"), + ("minor,preview", "1!0.1.0rc0"), + ("major,beta", "1!1.0.0b0"), + ("major,major,major", "1!3.0.0"), ], ) def test_correct(self, isolation, operations, expected): scheme = StandardScheme(str(isolation), {}) - assert scheme.update(operations, '1!0.0.1', {}) == expected + assert scheme.update(operations, "1!0.0.1", {}) == expected diff --git a/tests/backend/version/source/test_code.py b/tests/backend/version/source/test_code.py index 1e1cf639e..ceedba26f 100644 --- a/tests/backend/version/source/test_code.py +++ b/tests/backend/version/source/test_code.py @@ -6,83 +6,83 @@ def test_no_path(isolation): source = CodeSource(str(isolation), {}) - with pytest.raises(ValueError, match='option `path` must be specified'): + with pytest.raises(ValueError, match="option `path` must be specified"): source.get_version_data() def test_path_not_string(isolation): - source = CodeSource(str(isolation), {'path': 1}) + source = CodeSource(str(isolation), {"path": 1}) - with pytest.raises(TypeError, match='option `path` must be a string'): + with pytest.raises(TypeError, match="option `path` must be a string"): source.get_version_data() def test_path_nonexistent(isolation): - source = CodeSource(str(isolation), {'path': 'a/b.py'}) + source = CodeSource(str(isolation), {"path": "a/b.py"}) - with pytest.raises(OSError, match='file does not exist: a/b.py'): + with pytest.raises(OSError, match="file does not exist: a/b.py"): source.get_version_data() def test_expression_not_string(temp_dir): - source = CodeSource(str(temp_dir), {'path': 'a/b.py', 'expression': 23}) + source = CodeSource(str(temp_dir), {"path": "a/b.py", "expression": 23}) - file_path = temp_dir / 'a' / 'b.py' + file_path = temp_dir / "a" / "b.py" file_path.ensure_parent_dir_exists() file_path.touch() - with pytest.raises(TypeError, match='option `expression` must be a string'): + with pytest.raises(TypeError, match="option `expression` must be a string"): source.get_version_data() def test_search_paths_not_array(temp_dir): - source = CodeSource(str(temp_dir), {'path': 'a/b.py', 'search-paths': 23}) + source = CodeSource(str(temp_dir), {"path": "a/b.py", "search-paths": 23}) - file_path = temp_dir / 'a' / 'b.py' + file_path = temp_dir / "a" / "b.py" file_path.ensure_parent_dir_exists() file_path.touch() - with pytest.raises(TypeError, match='option `search-paths` must be an array'): + with pytest.raises(TypeError, match="option `search-paths` must be an array"): source.get_version_data() def test_search_paths_entry_not_string(temp_dir): - source = CodeSource(str(temp_dir), {'path': 'a/b.py', 'search-paths': [23]}) + source = CodeSource(str(temp_dir), {"path": "a/b.py", "search-paths": [23]}) - file_path = temp_dir / 'a' / 'b.py' + file_path = temp_dir / "a" / "b.py" file_path.ensure_parent_dir_exists() file_path.touch() - with pytest.raises(TypeError, match='entry #1 of option `search-paths` must be a string'): + with pytest.raises(TypeError, match="entry #1 of option `search-paths` must be a string"): source.get_version_data() def test_match_default_expression(temp_dir): - source = CodeSource(str(temp_dir), {'path': 'a/b.py'}) + source = CodeSource(str(temp_dir), {"path": "a/b.py"}) - file_path = temp_dir / 'a' / 'b.py' + file_path = temp_dir / "a" / "b.py" file_path.ensure_parent_dir_exists() file_path.write_text('__version__ = "0.0.1"') with temp_dir.as_cwd(): - assert source.get_version_data()['version'] == '0.0.1' + assert source.get_version_data()["version"] == "0.0.1" def test_match_custom_expression_basic(temp_dir): - source = CodeSource(str(temp_dir), {'path': 'a/b.py', 'expression': 'VER'}) + source = CodeSource(str(temp_dir), {"path": "a/b.py", "expression": "VER"}) - file_path = temp_dir / 'a' / 'b.py' + file_path = temp_dir / "a" / "b.py" file_path.ensure_parent_dir_exists() file_path.write_text('VER = "0.0.1"') with temp_dir.as_cwd(): - assert source.get_version_data()['version'] == '0.0.1' + assert source.get_version_data()["version"] == "0.0.1" def test_match_custom_expression_complex(temp_dir, helpers): - source = CodeSource(str(temp_dir), {'path': 'a/b.py', 'expression': 'foo()'}) + source = CodeSource(str(temp_dir), {"path": "a/b.py", "expression": "foo()"}) - file_path = temp_dir / 'a' / 'b.py' + file_path = temp_dir / "a" / "b.py" file_path.ensure_parent_dir_exists() file_path.write_text( helpers.dedent( @@ -96,16 +96,16 @@ def foo(): ) with temp_dir.as_cwd(): - assert source.get_version_data()['version'] == '1.0.0.1.dev0' + assert source.get_version_data()["version"] == "1.0.0.1.dev0" def test_search_paths(temp_dir, helpers): - source = CodeSource(str(temp_dir), {'path': 'a/b.py', 'search-paths': ['.']}) + source = CodeSource(str(temp_dir), {"path": "a/b.py", "search-paths": ["."]}) - parent_dir = temp_dir / 'a' + parent_dir = temp_dir / "a" parent_dir.mkdir() - (parent_dir / '__init__.py').touch() - (parent_dir / 'b.py').write_text( + (parent_dir / "__init__.py").touch() + (parent_dir / "b.py").write_text( helpers.dedent( """ from a.c import foo @@ -114,7 +114,7 @@ def test_search_paths(temp_dir, helpers): """ ) ) - (parent_dir / 'c.py').write_text( + (parent_dir / "c.py").write_text( helpers.dedent( """ def foo(version_info): @@ -124,4 +124,4 @@ def foo(version_info): ) with temp_dir.as_cwd(): - assert source.get_version_data()['version'] == '1.0.0.1.dev0' + assert source.get_version_data()["version"] == "1.0.0.1.dev0" diff --git a/tests/backend/version/source/test_env.py b/tests/backend/version/source/test_env.py index 3e8d74847..b275e9076 100644 --- a/tests/backend/version/source/test_env.py +++ b/tests/backend/version/source/test_env.py @@ -7,28 +7,29 @@ def test_no_variable(isolation): source = EnvSource(str(isolation), {}) - with pytest.raises(ValueError, match='option `variable` must be specified'): + with pytest.raises(ValueError, match="option `variable` must be specified"): source.get_version_data() def test_variable_not_string(isolation): - source = EnvSource(str(isolation), {'variable': 1}) + source = EnvSource(str(isolation), {"variable": 1}) - with pytest.raises(TypeError, match='option `variable` must be a string'): + with pytest.raises(TypeError, match="option `variable` must be a string"): source.get_version_data() def test_variable_not_available(isolation): - source = EnvSource(str(isolation), {'variable': 'ENV_VERSION'}) + source = EnvSource(str(isolation), {"variable": "ENV_VERSION"}) - with EnvVars(exclude=['ENV_VERSION']), pytest.raises( - RuntimeError, match='environment variable `ENV_VERSION` is not set' + with ( + EnvVars(exclude=["ENV_VERSION"]), + pytest.raises(RuntimeError, match="environment variable `ENV_VERSION` is not set"), ): source.get_version_data() def test_variable_contains_version(isolation): - source = EnvSource(str(isolation), {'variable': 'ENV_VERSION'}) + source = EnvSource(str(isolation), {"variable": "ENV_VERSION"}) - with EnvVars({'ENV_VERSION': '0.0.1'}): - assert source.get_version_data()['version'] == '0.0.1' + with EnvVars({"ENV_VERSION": "0.0.1"}): + assert source.get_version_data()["version"] == "0.0.1" diff --git a/tests/backend/version/source/test_regex.py b/tests/backend/version/source/test_regex.py index 9eecf3be2..325442f63 100644 --- a/tests/backend/version/source/test_regex.py +++ b/tests/backend/version/source/test_regex.py @@ -4,79 +4,79 @@ from hatchling.version.source.regex import RegexSource -DEFAULT_PATTERN_PRODUCTS = list(product(('__version__', 'VERSION', 'version'), ('"', "'"), ('', 'v'))) +DEFAULT_PATTERN_PRODUCTS = list(product(("__version__", "VERSION", "version"), ('"', "'"), ("", "v"))) def test_no_path(isolation): source = RegexSource(str(isolation), {}) - with pytest.raises(ValueError, match='option `path` must be specified'): + with pytest.raises(ValueError, match="option `path` must be specified"): source.get_version_data() def test_path_not_string(isolation): - source = RegexSource(str(isolation), {'path': 1}) + source = RegexSource(str(isolation), {"path": 1}) - with pytest.raises(TypeError, match='option `path` must be a string'): + with pytest.raises(TypeError, match="option `path` must be a string"): source.get_version_data() def test_path_nonexistent(isolation): - source = RegexSource(str(isolation), {'path': 'a/b'}) + source = RegexSource(str(isolation), {"path": "a/b"}) - with pytest.raises(OSError, match='file does not exist: a/b'): + with pytest.raises(OSError, match="file does not exist: a/b"): source.get_version_data() def test_pattern_not_string(temp_dir): - source = RegexSource(str(temp_dir), {'path': 'a/b', 'pattern': 23}) + source = RegexSource(str(temp_dir), {"path": "a/b", "pattern": 23}) - file_path = temp_dir / 'a' / 'b' + file_path = temp_dir / "a" / "b" file_path.ensure_parent_dir_exists() file_path.touch() - with pytest.raises(TypeError, match='option `pattern` must be a string'): + with pytest.raises(TypeError, match="option `pattern` must be a string"): source.get_version_data() def test_no_version(temp_dir): - source = RegexSource(str(temp_dir), {'path': 'a/b'}) + source = RegexSource(str(temp_dir), {"path": "a/b"}) - file_path = temp_dir / 'a' / 'b' + file_path = temp_dir / "a" / "b" file_path.ensure_parent_dir_exists() file_path.touch() - with temp_dir.as_cwd(), pytest.raises(ValueError, match='unable to parse the version from the file: a/b'): + with temp_dir.as_cwd(), pytest.raises(ValueError, match="unable to parse the version from the file: a/b"): source.get_version_data() def test_pattern_no_version_group(temp_dir): - source = RegexSource(str(temp_dir), {'path': 'a/b', 'pattern': '.+'}) + source = RegexSource(str(temp_dir), {"path": "a/b", "pattern": ".+"}) - file_path = temp_dir / 'a' / 'b' + file_path = temp_dir / "a" / "b" file_path.ensure_parent_dir_exists() - file_path.write_text('foo') + file_path.write_text("foo") - with temp_dir.as_cwd(), pytest.raises(ValueError, match='no group named `version` was defined in the pattern'): + with temp_dir.as_cwd(), pytest.raises(ValueError, match="no group named `version` was defined in the pattern"): source.get_version_data() def test_match_custom_pattern(temp_dir): - source = RegexSource(str(temp_dir), {'path': 'a/b', 'pattern': 'VER = "(?P.+)"'}) + source = RegexSource(str(temp_dir), {"path": "a/b", "pattern": 'VER = "(?P.+)"'}) - file_path = temp_dir / 'a' / 'b' + file_path = temp_dir / "a" / "b" file_path.ensure_parent_dir_exists() file_path.write_text('VER = "0.0.1"') with temp_dir.as_cwd(): - assert source.get_version_data()['version'] == '0.0.1' + assert source.get_version_data()["version"] == "0.0.1" -@pytest.mark.parametrize(('variable', 'quote', 'prefix'), DEFAULT_PATTERN_PRODUCTS) +@pytest.mark.parametrize(("variable", "quote", "prefix"), DEFAULT_PATTERN_PRODUCTS) def test_match_default_pattern(temp_dir, helpers, variable, quote, prefix): - source = RegexSource(str(temp_dir), {'path': 'a/b'}) + source = RegexSource(str(temp_dir), {"path": "a/b"}) - file_path = temp_dir / 'a' / 'b' + file_path = temp_dir / "a" / "b" file_path.ensure_parent_dir_exists() file_path.write_text( helpers.dedent( @@ -92,14 +92,14 @@ def foo(): ) with temp_dir.as_cwd(): - assert source.get_version_data()['version'] == '0.0.1' + assert source.get_version_data()["version"] == "0.0.1" -@pytest.mark.parametrize(('variable', 'quote', 'prefix'), DEFAULT_PATTERN_PRODUCTS) +@pytest.mark.parametrize(("variable", "quote", "prefix"), DEFAULT_PATTERN_PRODUCTS) def test_set_default_pattern(temp_dir, helpers, variable, quote, prefix): - source = RegexSource(str(temp_dir), {'path': 'a/b'}) + source = RegexSource(str(temp_dir), {"path": "a/b"}) - file_path = temp_dir / 'a' / 'b' + file_path = temp_dir / "a" / "b" file_path.ensure_parent_dir_exists() file_path.write_text( helpers.dedent( @@ -115,5 +115,5 @@ def foo(): ) with temp_dir.as_cwd(): - source.set_version('foo', source.get_version_data()) - assert source.get_version_data()['version'] == 'foo' + source.set_version("foo", source.get_version_data()) + assert source.get_version_data()["version"] == "foo" diff --git a/tests/cli/build/test_build.py b/tests/cli/build/test_build.py index 5f31c12fa..92fb76612 100644 --- a/tests/cli/build/test_build.py +++ b/tests/cli/build/test_build.py @@ -7,36 +7,36 @@ from hatch.project.constants import DEFAULT_BUILD_SCRIPT, DEFAULT_CONFIG_FILE, BuildEnvVars from hatch.project.core import Project -pytestmark = [pytest.mark.usefixtures('mock_backend_process')] +pytestmark = [pytest.mark.usefixtures("mock_backend_process")] @pytest.mark.requires_internet class TestOtherBackend: def test_standard(self, hatch, temp_dir, helpers): - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() project = Project(path) config = dict(project.raw_config) - config['build-system']['requires'] = ['flit-core'] - config['build-system']['build-backend'] = 'flit_core.buildapi' - config['project']['version'] = '0.0.1' - config['project']['dynamic'] = [] - del config['project']['license'] + config["build-system"]["requires"] = ["flit-core"] + config["build-system"]["build-backend"] = "flit_core.buildapi" + config["project"]["version"] = "0.0.1" + config["project"]["dynamic"] = [] + del config["project"]["license"] project.save_config(config) - build_directory = path / 'dist' + build_directory = path / "dist" assert not build_directory.is_dir() with path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('build') + result = hatch("build") assert result.exit_code == 0, result.output assert build_directory.is_dir() @@ -44,10 +44,10 @@ def test_standard(self, hatch, temp_dir, helpers): artifacts = list(build_directory.iterdir()) assert len(artifacts) == 2 - wheel_path = build_directory / 'my_app-0.0.1-py3-none-any.whl' + wheel_path = build_directory / "my_app-0.0.1-py3-none-any.whl" assert wheel_path.is_file() - sdist_path = build_directory / 'my_app-0.0.1.tar.gz' + sdist_path = build_directory / "my_app-0.0.1.tar.gz" assert sdist_path.is_file() assert result.exit_code == 0, result.output @@ -66,7 +66,7 @@ def test_standard(self, hatch, temp_dir, helpers): build_directory.remove() with path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('build', '-t', 'wheel') + result = hatch("build", "-t", "wheel") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -83,7 +83,7 @@ def test_standard(self, hatch, temp_dir, helpers): build_directory.remove() with path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('build', '-t', 'sdist') + result = hatch("build", "-t", "sdist") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -99,36 +99,36 @@ def test_standard(self, hatch, temp_dir, helpers): assert sdist_path.is_file() def test_legacy(self, hatch, temp_dir, helpers): - path = temp_dir / 'tmp' + path = temp_dir / "tmp" path.mkdir() - data_path = temp_dir / 'data' + data_path = temp_dir / "data" data_path.mkdir() - (path / 'pyproject.toml').write_text( + (path / "pyproject.toml").write_text( """\ [build-system] requires = ["setuptools"] build-backend = "setuptools.build_meta" """ ) - (path / 'setup.py').write_text( + (path / "setup.py").write_text( """\ import setuptools setuptools.setup(name="tmp", version="0.0.1") """ ) - (path / 'tmp.py').write_text( + (path / "tmp.py").write_text( """\ print("Hello World!") """ ) - (path / 'README.md').touch() + (path / "README.md").touch() - build_directory = path / 'dist' + build_directory = path / "dist" assert not build_directory.is_dir() with path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('build') + result = hatch("build") assert result.exit_code == 0, result.output assert build_directory.is_dir() @@ -136,10 +136,10 @@ def test_legacy(self, hatch, temp_dir, helpers): artifacts = list(build_directory.iterdir()) assert len(artifacts) == 2 - wheel_path = build_directory / 'tmp-0.0.1-py3-none-any.whl' + wheel_path = build_directory / "tmp-0.0.1-py3-none-any.whl" assert wheel_path.is_file() - sdist_path = build_directory / 'tmp-0.0.1.tar.gz' + sdist_path = build_directory / "tmp-0.0.1.tar.gz" assert sdist_path.is_file() assert result.exit_code == 0, result.output @@ -159,19 +159,19 @@ def test_legacy(self, hatch, temp_dir, helpers): @pytest.mark.allow_backend_process def test_incompatible_environment(hatch, temp_dir, helpers, build_env_config): - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - path = temp_dir / 'my-app' + path = temp_dir / "my-app" project = Project(path) - helpers.update_project_environment(project, 'hatch-build', {'python': '9000', **build_env_config}) + helpers.update_project_environment(project, "hatch-build", {"python": "9000", **build_env_config}) with path.as_cwd(): - result = hatch('build') + result = hatch("build") assert result.exit_code == 1, result.output assert result.output == helpers.dedent( @@ -184,21 +184,21 @@ def test_incompatible_environment(hatch, temp_dir, helpers, build_env_config): @pytest.mark.allow_backend_process @pytest.mark.requires_internet def test_no_compatibility_check_if_exists(hatch, temp_dir, helpers, mocker): - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('build') + result = hatch("build") assert result.exit_code == 0, result.output - build_directory = project_path / 'dist' + build_directory = project_path / "dist" assert build_directory.is_dir() artifacts = list(build_directory.iterdir()) @@ -217,9 +217,9 @@ def test_no_compatibility_check_if_exists(hatch, temp_dir, helpers, mocker): ) build_directory.remove() - mocker.patch('hatch.env.virtual.VirtualEnvironment.check_compatibility', side_effect=Exception('incompatible')) + mocker.patch("hatch.env.virtual.VirtualEnvironment.check_compatibility", side_effect=Exception("incompatible")) with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('build') + result = hatch("build") assert result.exit_code == 0, result.output artifacts = list(build_directory.iterdir()) @@ -236,16 +236,16 @@ def test_no_compatibility_check_if_exists(hatch, temp_dir, helpers, mocker): def test_unknown_targets(hatch, temp_dir, helpers): - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - path = temp_dir / 'my-app' + path = temp_dir / "my-app" with path.as_cwd(): - result = hatch('build', '-t', 'foo') + result = hatch("build", "-t", "foo") assert result.exit_code == 1, result.output assert result.output == helpers.dedent( @@ -261,16 +261,16 @@ def test_unknown_targets(hatch, temp_dir, helpers): def test_mutually_exclusive_hook_options(hatch, temp_dir, helpers): - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - path = temp_dir / 'my-app' + path = temp_dir / "my-app" with path.as_cwd(): - result = hatch('build', '--hooks-only', '--no-hooks') + result = hatch("build", "--hooks-only", "--no-hooks") assert result.exit_code == 1, result.output assert result.output == helpers.dedent( @@ -286,26 +286,26 @@ def test_mutually_exclusive_hook_options(hatch, temp_dir, helpers): def test_default(hatch, temp_dir, helpers): - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - path = temp_dir / 'my-app' + path = temp_dir / "my-app" with path.as_cwd(): - result = hatch('build') + result = hatch("build") assert result.exit_code == 0, result.output - build_directory = path / 'dist' + build_directory = path / "dist" assert build_directory.is_dir() artifacts = list(build_directory.iterdir()) assert len(artifacts) == 2 - sdist_path = next(artifact for artifact in artifacts if artifact.name.endswith('.tar.gz')) - wheel_path = next(artifact for artifact in artifacts if artifact.name.endswith('.whl')) + sdist_path = next(artifact for artifact in artifacts if artifact.name.endswith(".tar.gz")) + wheel_path = next(artifact for artifact in artifacts if artifact.name.endswith(".whl")) assert result.output == helpers.dedent( f""" @@ -322,25 +322,25 @@ def test_default(hatch, temp_dir, helpers): def test_explicit_targets(hatch, temp_dir, helpers): - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - path = temp_dir / 'my-app' + path = temp_dir / "my-app" with path.as_cwd(): - result = hatch('build', '-t', 'wheel') + result = hatch("build", "-t", "wheel") assert result.exit_code == 0, result.output - build_directory = path / 'dist' + build_directory = path / "dist" assert build_directory.is_dir() artifacts = list(build_directory.iterdir()) assert len(artifacts) == 1 - wheel_path = next(artifact for artifact in artifacts if artifact.name.endswith('.whl')) + wheel_path = next(artifact for artifact in artifacts if artifact.name.endswith(".whl")) assert result.output == helpers.dedent( f""" @@ -355,17 +355,17 @@ def test_explicit_targets(hatch, temp_dir, helpers): def test_explicit_directory(hatch, temp_dir, helpers): - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - path = temp_dir / 'my-app' - build_directory = temp_dir / 'dist' + path = temp_dir / "my-app" + build_directory = temp_dir / "dist" with path.as_cwd(): - result = hatch('build', str(build_directory)) + result = hatch("build", str(build_directory)) assert result.exit_code == 0, result.output assert build_directory.is_dir() @@ -373,8 +373,8 @@ def test_explicit_directory(hatch, temp_dir, helpers): artifacts = list(build_directory.iterdir()) assert len(artifacts) == 2 - sdist_path = next(artifact for artifact in artifacts if artifact.name.endswith('.tar.gz')) - wheel_path = next(artifact for artifact in artifacts if artifact.name.endswith('.whl')) + sdist_path = next(artifact for artifact in artifacts if artifact.name.endswith(".tar.gz")) + wheel_path = next(artifact for artifact in artifacts if artifact.name.endswith(".whl")) assert result.output == helpers.dedent( f""" @@ -391,17 +391,17 @@ def test_explicit_directory(hatch, temp_dir, helpers): def test_explicit_directory_env_var(hatch, temp_dir, helpers): - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - path = temp_dir / 'my-app' - build_directory = temp_dir / 'dist' + path = temp_dir / "my-app" + build_directory = temp_dir / "dist" with path.as_cwd({BuildEnvVars.LOCATION: str(build_directory)}): - result = hatch('build') + result = hatch("build") assert result.exit_code == 0, result.output assert build_directory.is_dir() @@ -409,8 +409,8 @@ def test_explicit_directory_env_var(hatch, temp_dir, helpers): artifacts = list(build_directory.iterdir()) assert len(artifacts) == 2 - sdist_path = next(artifact for artifact in artifacts if artifact.name.endswith('.tar.gz')) - wheel_path = next(artifact for artifact in artifacts if artifact.name.endswith('.whl')) + sdist_path = next(artifact for artifact in artifacts if artifact.name.endswith(".tar.gz")) + wheel_path = next(artifact for artifact in artifacts if artifact.name.endswith(".whl")) assert result.output == helpers.dedent( f""" @@ -427,16 +427,16 @@ def test_explicit_directory_env_var(hatch, temp_dir, helpers): def test_clean(hatch, temp_dir, helpers, config_file): - config_file.model.template.plugins['default']['src-layout'] = False + config_file.model.template.plugins["default"]["src-layout"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - path = temp_dir / 'my-app' + path = temp_dir / "my-app" build_script = path / DEFAULT_BUILD_SCRIPT build_script.write_text( @@ -459,45 +459,45 @@ def initialize(self, version, build_data): project = Project(path) config = dict(project.raw_config) - config['tool']['hatch']['build'] = {'hooks': {'custom': {'path': build_script.name}}} + config["tool"]["hatch"]["build"] = {"hooks": {"custom": {"path": build_script.name}}} project.save_config(config) with path.as_cwd(): - result = hatch('build') + result = hatch("build") assert result.exit_code == 0, result.output - result = hatch('version', 'minor') + result = hatch("version", "minor") assert result.exit_code == 0, result.output - result = hatch('build') + result = hatch("build") assert result.exit_code == 0, result.output - build_directory = path / 'dist' + build_directory = path / "dist" assert build_directory.is_dir() - assert (path / 'my_app' / 'lib.so').is_file() + assert (path / "my_app" / "lib.so").is_file() artifacts = list(build_directory.iterdir()) assert len(artifacts) == 4 - test_file = build_directory / 'test.txt' + test_file = build_directory / "test.txt" test_file.touch() with path.as_cwd(): - result = hatch('version', '9000') + result = hatch("version", "9000") assert result.exit_code == 0, result.output - result = hatch('build', '-c') + result = hatch("build", "-c") assert result.exit_code == 0, result.output artifacts = list(build_directory.iterdir()) assert len(artifacts) == 3 assert test_file in artifacts - sdist_path = next(artifact for artifact in artifacts if artifact.name.endswith('.tar.gz')) - assert '9000' in str(sdist_path) + sdist_path = next(artifact for artifact in artifacts if artifact.name.endswith(".tar.gz")) + assert "9000" in str(sdist_path) - wheel_path = next(artifact for artifact in artifacts if artifact.name.endswith('.whl')) - assert '9000' in str(wheel_path) + wheel_path = next(artifact for artifact in artifacts if artifact.name.endswith(".whl")) + assert "9000" in str(wheel_path) assert result.output == helpers.dedent( f""" @@ -511,49 +511,49 @@ def initialize(self, version, build_data): def test_clean_env_var(hatch, temp_dir, helpers): - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - path = temp_dir / 'my-app' + path = temp_dir / "my-app" with path.as_cwd(): - result = hatch('build') + result = hatch("build") assert result.exit_code == 0, result.output - result = hatch('version', 'minor') + result = hatch("version", "minor") assert result.exit_code == 0, result.output - result = hatch('build') + result = hatch("build") assert result.exit_code == 0, result.output - build_directory = path / 'dist' + build_directory = path / "dist" assert build_directory.is_dir() artifacts = list(build_directory.iterdir()) assert len(artifacts) == 4 - test_file = build_directory / 'test.txt' + test_file = build_directory / "test.txt" test_file.touch() - with path.as_cwd({BuildEnvVars.CLEAN: 'true'}): - result = hatch('version', '9000') + with path.as_cwd({BuildEnvVars.CLEAN: "true"}): + result = hatch("version", "9000") assert result.exit_code == 0, result.output - result = hatch('build') + result = hatch("build") assert result.exit_code == 0, result.output artifacts = list(build_directory.iterdir()) assert len(artifacts) == 3 assert test_file in artifacts - sdist_path = next(artifact for artifact in artifacts if artifact.name.endswith('.tar.gz')) - assert '9000' in str(sdist_path) + sdist_path = next(artifact for artifact in artifacts if artifact.name.endswith(".tar.gz")) + assert "9000" in str(sdist_path) - wheel_path = next(artifact for artifact in artifacts if artifact.name.endswith('.whl')) - assert '9000' in str(wheel_path) + wheel_path = next(artifact for artifact in artifacts if artifact.name.endswith(".whl")) + assert "9000" in str(wheel_path) assert result.output == helpers.dedent( f""" @@ -567,16 +567,16 @@ def test_clean_env_var(hatch, temp_dir, helpers): def test_clean_only(hatch, temp_dir, helpers, config_file): - config_file.model.template.plugins['default']['src-layout'] = False + config_file.model.template.plugins["default"]["src-layout"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - path = temp_dir / 'my-app' + path = temp_dir / "my-app" build_script = path / DEFAULT_BUILD_SCRIPT build_script.write_text( @@ -599,26 +599,26 @@ def initialize(self, version, build_data): project = Project(path) config = dict(project.raw_config) - config['tool']['hatch']['build'] = {'hooks': {'custom': {'path': build_script.name}}} + config["tool"]["hatch"]["build"] = {"hooks": {"custom": {"path": build_script.name}}} project.save_config(config) with path.as_cwd(): - result = hatch('build') + result = hatch("build") assert result.exit_code == 0, result.output - build_directory = path / 'dist' + build_directory = path / "dist" assert build_directory.is_dir() - build_artifact = path / 'my_app' / 'lib.so' + build_artifact = path / "my_app" / "lib.so" assert build_artifact.is_file() artifacts = list(build_directory.iterdir()) assert len(artifacts) == 2 with path.as_cwd(): - result = hatch('version', 'minor') + result = hatch("version", "minor") assert result.exit_code == 0, result.output - result = hatch('build', '--clean-only') + result = hatch("build", "--clean-only") assert result.exit_code == 0, result.output artifacts = list(build_directory.iterdir()) @@ -633,16 +633,16 @@ def initialize(self, version, build_data): def test_clean_only_hooks_only(hatch, temp_dir, helpers, config_file): - config_file.model.template.plugins['default']['src-layout'] = False + config_file.model.template.plugins["default"]["src-layout"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - path = temp_dir / 'my-app' + path = temp_dir / "my-app" build_script = path / DEFAULT_BUILD_SCRIPT build_script.write_text( @@ -665,26 +665,26 @@ def initialize(self, version, build_data): project = Project(path) config = dict(project.raw_config) - config['tool']['hatch']['build'] = {'hooks': {'custom': {'path': build_script.name}}} + config["tool"]["hatch"]["build"] = {"hooks": {"custom": {"path": build_script.name}}} project.save_config(config) with path.as_cwd(): - result = hatch('build') + result = hatch("build") assert result.exit_code == 0, result.output - build_directory = path / 'dist' + build_directory = path / "dist" assert build_directory.is_dir() - build_artifact = path / 'my_app' / 'lib.so' + build_artifact = path / "my_app" / "lib.so" assert build_artifact.is_file() artifacts = list(build_directory.iterdir()) assert len(artifacts) == 2 with path.as_cwd(): - result = hatch('version', 'minor') + result = hatch("version", "minor") assert result.exit_code == 0, result.output - result = hatch('build', '--clean-only', '--hooks-only') + result = hatch("build", "--clean-only", "--hooks-only") assert result.exit_code == 0, result.output artifacts = list(build_directory.iterdir()) @@ -699,16 +699,16 @@ def initialize(self, version, build_data): def test_clean_hooks_after(hatch, temp_dir, helpers, config_file): - config_file.model.template.plugins['default']['src-layout'] = False + config_file.model.template.plugins["default"]["src-layout"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - path = temp_dir / 'my-app' + path = temp_dir / "my-app" build_script = path / DEFAULT_BUILD_SCRIPT build_script.write_text( @@ -731,23 +731,23 @@ def initialize(self, version, build_data): project = Project(path) config = dict(project.raw_config) - config['tool']['hatch']['build'] = {'hooks': {'custom': {'path': build_script.name}}} + config["tool"]["hatch"]["build"] = {"hooks": {"custom": {"path": build_script.name}}} project.save_config(config) with path.as_cwd(): - result = hatch('build', '--clean-hooks-after') + result = hatch("build", "--clean-hooks-after") assert result.exit_code == 0, result.output - build_directory = path / 'dist' + build_directory = path / "dist" assert build_directory.is_dir() - build_artifact = path / 'my_app' / 'lib.so' + build_artifact = path / "my_app" / "lib.so" assert not build_artifact.exists() artifacts = list(build_directory.iterdir()) assert len(artifacts) == 2 - sdist_path = next(artifact for artifact in artifacts if artifact.name.endswith('.tar.gz')) - wheel_path = next(artifact for artifact in artifacts if artifact.name.endswith('.whl')) + sdist_path = next(artifact for artifact in artifacts if artifact.name.endswith(".tar.gz")) + wheel_path = next(artifact for artifact in artifacts if artifact.name.endswith(".whl")) assert result.output == helpers.dedent( f""" @@ -764,16 +764,16 @@ def initialize(self, version, build_data): def test_clean_hooks_after_env_var(hatch, temp_dir, helpers, config_file): - config_file.model.template.plugins['default']['src-layout'] = False + config_file.model.template.plugins["default"]["src-layout"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - path = temp_dir / 'my-app' + path = temp_dir / "my-app" build_script = path / DEFAULT_BUILD_SCRIPT build_script.write_text( @@ -796,23 +796,23 @@ def initialize(self, version, build_data): project = Project(path) config = dict(project.raw_config) - config['tool']['hatch']['build'] = {'hooks': {'custom': {'path': build_script.name}}} + config["tool"]["hatch"]["build"] = {"hooks": {"custom": {"path": build_script.name}}} project.save_config(config) - with path.as_cwd({BuildEnvVars.CLEAN_HOOKS_AFTER: 'true'}): - result = hatch('build') + with path.as_cwd({BuildEnvVars.CLEAN_HOOKS_AFTER: "true"}): + result = hatch("build") assert result.exit_code == 0, result.output - build_directory = path / 'dist' + build_directory = path / "dist" assert build_directory.is_dir() - build_artifact = path / 'my_app' / 'lib.so' + build_artifact = path / "my_app" / "lib.so" assert not build_artifact.exists() artifacts = list(build_directory.iterdir()) assert len(artifacts) == 2 - sdist_path = next(artifact for artifact in artifacts if artifact.name.endswith('.tar.gz')) - wheel_path = next(artifact for artifact in artifacts if artifact.name.endswith('.whl')) + sdist_path = next(artifact for artifact in artifacts if artifact.name.endswith(".tar.gz")) + wheel_path = next(artifact for artifact in artifacts if artifact.name.endswith(".whl")) assert result.output == helpers.dedent( f""" @@ -829,16 +829,16 @@ def initialize(self, version, build_data): def test_clean_only_no_hooks(hatch, temp_dir, helpers, config_file): - config_file.model.template.plugins['default']['src-layout'] = False + config_file.model.template.plugins["default"]["src-layout"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - path = temp_dir / 'my-app' + path = temp_dir / "my-app" build_script = path / DEFAULT_BUILD_SCRIPT build_script.write_text( @@ -861,26 +861,26 @@ def initialize(self, version, build_data): project = Project(path) config = dict(project.raw_config) - config['tool']['hatch']['build'] = {'hooks': {'custom': {'path': build_script.name}}} + config["tool"]["hatch"]["build"] = {"hooks": {"custom": {"path": build_script.name}}} project.save_config(config) with path.as_cwd(): - result = hatch('build') + result = hatch("build") assert result.exit_code == 0, result.output - build_directory = path / 'dist' + build_directory = path / "dist" assert build_directory.is_dir() - build_artifact = path / 'my_app' / 'lib.so' + build_artifact = path / "my_app" / "lib.so" assert build_artifact.is_file() artifacts = list(build_directory.iterdir()) assert len(artifacts) == 2 with path.as_cwd(): - result = hatch('version', 'minor') + result = hatch("version", "minor") assert result.exit_code == 0, result.output - result = hatch('build', '--clean-only', '--no-hooks') + result = hatch("build", "--clean-only", "--no-hooks") assert result.exit_code == 0, result.output artifacts = list(build_directory.iterdir()) @@ -895,16 +895,16 @@ def initialize(self, version, build_data): def test_hooks_only(hatch, temp_dir, helpers, config_file): - config_file.model.template.plugins['default']['src-layout'] = False + config_file.model.template.plugins["default"]["src-layout"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - path = temp_dir / 'my-app' + path = temp_dir / "my-app" build_script = path / DEFAULT_BUILD_SCRIPT build_script.write_text( @@ -924,19 +924,19 @@ def initialize(self, version, build_data): project = Project(path) config = dict(project.raw_config) - config['tool']['hatch']['build'] = {'hooks': {'custom': {'path': build_script.name}}} + config["tool"]["hatch"]["build"] = {"hooks": {"custom": {"path": build_script.name}}} project.save_config(config) with path.as_cwd(): - result = hatch('-v', 'build', '-t', 'wheel', '--hooks-only') + result = hatch("-v", "build", "-t", "wheel", "--hooks-only") assert result.exit_code == 0, result.output - build_directory = path / 'dist' + build_directory = path / "dist" assert build_directory.is_dir() artifacts = list(build_directory.iterdir()) assert len(artifacts) == 0 - assert (path / 'my_app' / 'lib.so').is_file() + assert (path / "my_app" / "lib.so").is_file() helpers.assert_output_match( result.output, @@ -955,16 +955,16 @@ def initialize(self, version, build_data): def test_hooks_only_env_var(hatch, temp_dir, helpers, config_file): - config_file.model.template.plugins['default']['src-layout'] = False + config_file.model.template.plugins["default"]["src-layout"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - path = temp_dir / 'my-app' + path = temp_dir / "my-app" build_script = path / DEFAULT_BUILD_SCRIPT build_script.write_text( @@ -984,19 +984,19 @@ def initialize(self, version, build_data): project = Project(path) config = dict(project.raw_config) - config['tool']['hatch']['build'] = {'hooks': {'custom': {'path': build_script.name}}} + config["tool"]["hatch"]["build"] = {"hooks": {"custom": {"path": build_script.name}}} project.save_config(config) - with path.as_cwd({BuildEnvVars.HOOKS_ONLY: 'true'}): - result = hatch('-v', 'build', '-t', 'wheel') + with path.as_cwd({BuildEnvVars.HOOKS_ONLY: "true"}): + result = hatch("-v", "build", "-t", "wheel") assert result.exit_code == 0, result.output - build_directory = path / 'dist' + build_directory = path / "dist" assert build_directory.is_dir() artifacts = list(build_directory.iterdir()) assert len(artifacts) == 0 - assert (path / 'my_app' / 'lib.so').is_file() + assert (path / "my_app" / "lib.so").is_file() helpers.assert_output_match( result.output, @@ -1015,16 +1015,16 @@ def initialize(self, version, build_data): def test_extensions_only(hatch, temp_dir, helpers, config_file): - config_file.model.template.plugins['default']['src-layout'] = False + config_file.model.template.plugins["default"]["src-layout"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - path = temp_dir / 'my-app' + path = temp_dir / "my-app" build_script = path / DEFAULT_BUILD_SCRIPT build_script.write_text( @@ -1044,19 +1044,19 @@ def initialize(self, version, build_data): project = Project(path) config = dict(project.raw_config) - config['tool']['hatch']['build'] = {'hooks': {'custom': {'path': build_script.name}}} + config["tool"]["hatch"]["build"] = {"hooks": {"custom": {"path": build_script.name}}} project.save_config(config) with path.as_cwd(): - result = hatch('-v', 'build', '--ext') + result = hatch("-v", "build", "--ext") assert result.exit_code == 0, result.output - build_directory = path / 'dist' + build_directory = path / "dist" assert build_directory.is_dir() artifacts = list(build_directory.iterdir()) assert len(artifacts) == 0 - assert (path / 'my_app' / 'lib.so').is_file() + assert (path / "my_app" / "lib.so").is_file() helpers.assert_output_match( result.output, @@ -1075,13 +1075,13 @@ def initialize(self, version, build_data): def test_no_hooks(hatch, temp_dir, helpers): - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - path = temp_dir / 'my-app' + path = temp_dir / "my-app" build_script = path / DEFAULT_BUILD_SCRIPT build_script.write_text( @@ -1101,21 +1101,21 @@ def initialize(self, version, build_data): project = Project(path) config = dict(project.raw_config) - config['tool']['hatch']['build'] = {'hooks': {'custom': {'path': build_script.name}}} + config["tool"]["hatch"]["build"] = {"hooks": {"custom": {"path": build_script.name}}} project.save_config(config) with path.as_cwd(): - result = hatch('build', '-t', 'wheel', '--no-hooks') + result = hatch("build", "-t", "wheel", "--no-hooks") assert result.exit_code == 0, result.output - build_directory = path / 'dist' + build_directory = path / "dist" assert build_directory.is_dir() artifacts = list(build_directory.iterdir()) assert len(artifacts) == 1 - assert not (path / 'my_app' / 'lib.so').exists() + assert not (path / "my_app" / "lib.so").exists() - wheel_path = next(artifact for artifact in artifacts if artifact.name.endswith('.whl')) + wheel_path = next(artifact for artifact in artifacts if artifact.name.endswith(".whl")) assert result.output == helpers.dedent( f""" @@ -1130,13 +1130,13 @@ def initialize(self, version, build_data): def test_no_hooks_env_var(hatch, temp_dir, helpers): - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - path = temp_dir / 'my-app' + path = temp_dir / "my-app" build_script = path / DEFAULT_BUILD_SCRIPT build_script.write_text( @@ -1156,21 +1156,21 @@ def initialize(self, version, build_data): project = Project(path) config = dict(project.raw_config) - config['tool']['hatch']['build'] = {'hooks': {'custom': {'path': build_script.name}}} + config["tool"]["hatch"]["build"] = {"hooks": {"custom": {"path": build_script.name}}} project.save_config(config) - with path.as_cwd({BuildEnvVars.NO_HOOKS: 'true'}): - result = hatch('build', '-t', 'wheel') + with path.as_cwd({BuildEnvVars.NO_HOOKS: "true"}): + result = hatch("build", "-t", "wheel") assert result.exit_code == 0, result.output - build_directory = path / 'dist' + build_directory = path / "dist" assert build_directory.is_dir() artifacts = list(build_directory.iterdir()) assert len(artifacts) == 1 - assert not (path / 'my_app' / 'lib.so').exists() + assert not (path / "my_app" / "lib.so").exists() - wheel_path = next(artifact for artifact in artifacts if artifact.name.endswith('.whl')) + wheel_path = next(artifact for artifact in artifacts if artifact.name.endswith(".whl")) assert result.output == helpers.dedent( f""" @@ -1185,25 +1185,25 @@ def initialize(self, version, build_data): def test_debug_verbosity(hatch, temp_dir, helpers): - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - path = temp_dir / 'my-app' + path = temp_dir / "my-app" with path.as_cwd(): - result = hatch('-v', 'build', '-t', 'wheel:standard') + result = hatch("-v", "build", "-t", "wheel:standard") assert result.exit_code == 0, result.output - build_directory = path / 'dist' + build_directory = path / "dist" assert build_directory.is_dir() artifacts = list(build_directory.iterdir()) assert len(artifacts) == 1 - wheel_path = next(artifact for artifact in artifacts if artifact.name.endswith('.whl')) + wheel_path = next(artifact for artifact in artifacts if artifact.name.endswith(".whl")) helpers.assert_output_match( result.output, @@ -1223,21 +1223,21 @@ def test_debug_verbosity(hatch, temp_dir, helpers): @pytest.mark.allow_backend_process @pytest.mark.requires_internet def test_shipped(hatch, temp_dir, helpers): - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('build') + result = hatch("build") assert result.exit_code == 0, result.output - env_data_path = data_path / 'env' / 'virtual' + env_data_path = data_path / "env" / "virtual" assert env_data_path.is_dir() project_data_path = env_data_path / project_path.name @@ -1254,9 +1254,9 @@ def test_shipped(hatch, temp_dir, helpers): env_path = env_dirs[0] - assert env_path.name == 'hatch-build' + assert env_path.name == "hatch-build" - build_directory = project_path / 'dist' + build_directory = project_path / "dist" assert build_directory.is_dir() artifacts = list(build_directory.iterdir()) @@ -1275,7 +1275,7 @@ def test_shipped(hatch, temp_dir, helpers): # Test removal while we're here with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('env', 'remove', 'hatch-build') + result = hatch("env", "remove", "hatch-build") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -1290,14 +1290,14 @@ def test_shipped(hatch, temp_dir, helpers): @pytest.mark.allow_backend_process @pytest.mark.requires_internet def test_build_dependencies(hatch, temp_dir, helpers): - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() build_script = project_path / DEFAULT_BUILD_SCRIPT @@ -1322,22 +1322,22 @@ def build(self, **kwargs): project = Project(project_path) config = dict(project.raw_config) - config['tool']['hatch']['build'] = { - 'targets': {'custom': {'dependencies': ['binary'], 'path': DEFAULT_BUILD_SCRIPT}}, + config["tool"]["hatch"]["build"] = { + "targets": {"custom": {"dependencies": ["binary"], "path": DEFAULT_BUILD_SCRIPT}}, } project.save_config(config) with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('build', '-t', 'custom') + result = hatch("build", "-t", "custom") assert result.exit_code == 0, result.output - build_directory = project_path / 'dist' + build_directory = project_path / "dist" assert build_directory.is_dir() artifacts = list(build_directory.iterdir()) assert len(artifacts) == 1 - output_file = project_path / 'test.txt' + output_file = project_path / "test.txt" assert output_file.is_file() assert str(output_file.read_text()) == "(1.0, 'KiB')" @@ -1355,13 +1355,13 @@ def build(self, **kwargs): def test_plugin_dependencies_unmet(hatch, temp_dir, helpers, mock_plugin_installation): - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - path = temp_dir / 'my-app' + path = temp_dir / "my-app" dependency = os.urandom(16).hex() (path / DEFAULT_CONFIG_FILE).write_text( @@ -1374,17 +1374,17 @@ def test_plugin_dependencies_unmet(hatch, temp_dir, helpers, mock_plugin_install ) with path.as_cwd(): - result = hatch('build') + result = hatch("build") assert result.exit_code == 0, result.output - build_directory = path / 'dist' + build_directory = path / "dist" assert build_directory.is_dir() artifacts = list(build_directory.iterdir()) assert len(artifacts) == 2 - sdist_path = next(artifact for artifact in artifacts if artifact.name.endswith('.tar.gz')) - wheel_path = next(artifact for artifact in artifacts if artifact.name.endswith('.whl')) + sdist_path = next(artifact for artifact in artifacts if artifact.name.endswith(".tar.gz")) + wheel_path = next(artifact for artifact in artifacts if artifact.name.endswith(".whl")) assert result.output == helpers.dedent( f""" diff --git a/tests/cli/clean/test_clean.py b/tests/cli/clean/test_clean.py index e5c4d2558..3228e4789 100644 --- a/tests/cli/clean/test_clean.py +++ b/tests/cli/clean/test_clean.py @@ -5,20 +5,20 @@ from hatch.project.core import Project from hatchling.utils.constants import DEFAULT_BUILD_SCRIPT, DEFAULT_CONFIG_FILE -pytestmark = [pytest.mark.usefixtures('mock_backend_process')] +pytestmark = [pytest.mark.usefixtures("mock_backend_process")] def test(hatch, temp_dir, helpers, config_file, mock_plugin_installation): - config_file.model.template.plugins['default']['src-layout'] = False + config_file.model.template.plugins["default"]["src-layout"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - path = temp_dir / 'my-app' + path = temp_dir / "my-app" build_script = path / DEFAULT_BUILD_SCRIPT build_script.write_text( @@ -41,16 +41,16 @@ def initialize(self, version, build_data): project = Project(path) config = dict(project.raw_config) - config['tool']['hatch']['build'] = {'hooks': {'custom': {'path': build_script.name}}} + config["tool"]["hatch"]["build"] = {"hooks": {"custom": {"path": build_script.name}}} project.save_config(config) with path.as_cwd(): - result = hatch('build') + result = hatch("build") assert result.exit_code == 0, result.output - build_directory = path / 'dist' + build_directory = path / "dist" assert build_directory.is_dir() - build_artifact = path / 'my_app' / 'lib.so' + build_artifact = path / "my_app" / "lib.so" assert build_artifact.is_file() artifacts = list(build_directory.iterdir()) @@ -67,10 +67,10 @@ def initialize(self, version, build_data): ) with path.as_cwd(): - result = hatch('version', 'minor') + result = hatch("version", "minor") assert result.exit_code == 0, result.output - result = hatch('clean') + result = hatch("clean") assert result.exit_code == 0, result.output artifacts = list(build_directory.iterdir()) diff --git a/tests/cli/config/test_explore.py b/tests/cli/config/test_explore.py index 15610e7fe..959718072 100644 --- a/tests/cli/config/test_explore.py +++ b/tests/cli/config/test_explore.py @@ -1,6 +1,6 @@ def test_call(hatch, config_file, mocker): - mock = mocker.patch('click.launch') - result = hatch('config', 'explore') + mock = mocker.patch("click.launch") + result = hatch("config", "explore") assert result.exit_code == 0, result.output mock.assert_called_once_with(str(config_file.path), locate=True) diff --git a/tests/cli/config/test_find.py b/tests/cli/config/test_find.py index 30b52cdb5..62abc69ed 100644 --- a/tests/cli/config/test_find.py +++ b/tests/cli/config/test_find.py @@ -1,5 +1,5 @@ def test(hatch, config_file, helpers): - result = hatch('config', 'find') + result = hatch("config", "find") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( diff --git a/tests/cli/config/test_restore.py b/tests/cli/config/test_restore.py index 04b1257cc..7f890b8ea 100644 --- a/tests/cli/config/test_restore.py +++ b/tests/cli/config/test_restore.py @@ -1,21 +1,21 @@ def test_standard(hatch, config_file): - config_file.model.project = 'foo' + config_file.model.project = "foo" config_file.save() - result = hatch('config', 'restore') + result = hatch("config", "restore") assert result.exit_code == 0, result.output - assert result.output == 'Settings were successfully restored.\n' + assert result.output == "Settings were successfully restored.\n" config_file.load() - assert config_file.model.project == '' + assert config_file.model.project == "" def test_allow_invalid_config(hatch, config_file, helpers): - config_file.model.project = ['foo'] + config_file.model.project = ["foo"] config_file.save() - result = hatch('config', 'restore') + result = hatch("config", "restore") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( diff --git a/tests/cli/config/test_set.py b/tests/cli/config/test_set.py index 73bb9bc03..953e1cbe4 100644 --- a/tests/cli/config/test_set.py +++ b/tests/cli/config/test_set.py @@ -1,5 +1,5 @@ def test_standard(hatch, config_file, helpers): - result = hatch('config', 'set', 'project', 'foo') + result = hatch("config", "set", "project", "foo") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -10,11 +10,11 @@ def test_standard(hatch, config_file, helpers): ) config_file.load() - assert config_file.model.project == 'foo' + assert config_file.model.project == "foo" def test_standard_deep(hatch, config_file, helpers): - result = hatch('config', 'set', 'template.name', 'foo') + result = hatch("config", "set", "template.name", "foo") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -26,11 +26,11 @@ def test_standard_deep(hatch, config_file, helpers): ) config_file.load() - assert config_file.model.template.name == 'foo' + assert config_file.model.template.name == "foo" def test_standard_complex_sequence(hatch, config_file, helpers): - result = hatch('config', 'set', 'dirs.project', "['/foo', '/bar']") + result = hatch("config", "set", "dirs.project", "['/foo', '/bar']") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -42,11 +42,11 @@ def test_standard_complex_sequence(hatch, config_file, helpers): ) config_file.load() - assert config_file.model.dirs.project == ['/foo', '/bar'] + assert config_file.model.dirs.project == ["/foo", "/bar"] def test_standard_complex_map(hatch, config_file, helpers): - result = hatch('config', 'set', 'projects', "{'a': '/foo', 'b': '/bar'}") + result = hatch("config", "set", "projects", "{'a': '/foo', 'b': '/bar'}") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -59,12 +59,12 @@ def test_standard_complex_map(hatch, config_file, helpers): ) config_file.load() - assert config_file.model.projects['a'].location == '/foo' - assert config_file.model.projects['b'].location == '/bar' + assert config_file.model.projects["a"].location == "/foo" + assert config_file.model.projects["b"].location == "/bar" def test_standard_hidden(hatch, config_file, helpers): - result = hatch('config', 'set', 'publish.index.auth', 'foo') + result = hatch("config", "set", "publish.index.auth", "foo") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -76,11 +76,11 @@ def test_standard_hidden(hatch, config_file, helpers): ) config_file.load() - assert config_file.model.publish['index']['auth'] == 'foo' + assert config_file.model.publish["index"]["auth"] == "foo" def test_prompt(hatch, config_file, helpers): - result = hatch('config', 'set', 'project', input='foo') + result = hatch("config", "set", "project", input="foo") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -92,16 +92,16 @@ def test_prompt(hatch, config_file, helpers): ) config_file.load() - assert config_file.model.project == 'foo' + assert config_file.model.project == "foo" def test_prompt_hidden(hatch, config_file, helpers): - result = hatch('config', 'set', 'publish.index.auth', input='foo') + result = hatch("config", "set", "publish.index.auth", input="foo") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( f""" - Value for `publish.index.auth`:{' '} + Value for `publish.index.auth`:{" "} New setting: [publish.index] auth = "<...>" @@ -109,12 +109,12 @@ def test_prompt_hidden(hatch, config_file, helpers): ) config_file.load() - assert config_file.model.publish['index']['auth'] == 'foo' + assert config_file.model.publish["index"]["auth"] == "foo" def test_prevent_invalid_config(hatch, config_file, helpers): original_mode = config_file.model.mode - result = hatch('config', 'set', 'mode', 'foo') + result = hatch("config", "set", "mode", "foo") assert result.exit_code == 1 assert result.output == helpers.dedent( @@ -130,13 +130,13 @@ def test_prevent_invalid_config(hatch, config_file, helpers): def test_resolve_project_location_basic(hatch, config_file, helpers, temp_dir): - config_file.model.project = 'foo' + config_file.model.project = "foo" config_file.save() with temp_dir.as_cwd(): - result = hatch('config', 'set', 'projects.foo', '.') + result = hatch("config", "set", "projects.foo", ".") - path = str(temp_dir).replace('\\', '\\\\') + path = str(temp_dir).replace("\\", "\\\\") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -148,17 +148,17 @@ def test_resolve_project_location_basic(hatch, config_file, helpers, temp_dir): ) config_file.load() - assert config_file.model.projects['foo'].location == str(temp_dir) + assert config_file.model.projects["foo"].location == str(temp_dir) def test_resolve_project_location_complex(hatch, config_file, helpers, temp_dir): - config_file.model.project = 'foo' + config_file.model.project = "foo" config_file.save() with temp_dir.as_cwd(): - result = hatch('config', 'set', 'projects.foo.location', '.') + result = hatch("config", "set", "projects.foo.location", ".") - path = str(temp_dir).replace('\\', '\\\\') + path = str(temp_dir).replace("\\", "\\\\") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -170,14 +170,14 @@ def test_resolve_project_location_complex(hatch, config_file, helpers, temp_dir) ) config_file.load() - assert config_file.model.projects['foo'].location == str(temp_dir) + assert config_file.model.projects["foo"].location == str(temp_dir) def test_project_location_basic_set_first_project(hatch, config_file, helpers, temp_dir): with temp_dir.as_cwd(): - result = hatch('config', 'set', 'projects.foo', '.') + result = hatch("config", "set", "projects.foo", ".") - path = str(temp_dir).replace('\\', '\\\\') + path = str(temp_dir).replace("\\", "\\\\") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -191,15 +191,15 @@ def test_project_location_basic_set_first_project(hatch, config_file, helpers, t ) config_file.load() - assert config_file.model.project == 'foo' - assert config_file.model.projects['foo'].location == str(temp_dir) + assert config_file.model.project == "foo" + assert config_file.model.projects["foo"].location == str(temp_dir) def test_project_location_complex_set_first_project(hatch, config_file, helpers, temp_dir): with temp_dir.as_cwd(): - result = hatch('config', 'set', 'projects.foo.location', '.') + result = hatch("config", "set", "projects.foo.location", ".") - path = str(temp_dir).replace('\\', '\\\\') + path = str(temp_dir).replace("\\", "\\\\") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -213,15 +213,15 @@ def test_project_location_complex_set_first_project(hatch, config_file, helpers, ) config_file.load() - assert config_file.model.project == 'foo' - assert config_file.model.projects['foo'].location == str(temp_dir) + assert config_file.model.project == "foo" + assert config_file.model.projects["foo"].location == str(temp_dir) def test_booleans(hatch, config_file, helpers, temp_dir): assert config_file.model.template.licenses.headers is True with temp_dir.as_cwd(): - result = hatch('config', 'set', 'template.licenses.headers', 'false') + result = hatch("config", "set", "template.licenses.headers", "false") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -236,7 +236,7 @@ def test_booleans(hatch, config_file, helpers, temp_dir): assert config_file.model.template.licenses.headers is False with temp_dir.as_cwd(): - result = hatch('config', 'set', 'template.licenses.headers', 'TruE') + result = hatch("config", "set", "template.licenses.headers", "TruE") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( diff --git a/tests/cli/config/test_show.py b/tests/cli/config/test_show.py index 809ad8d2c..7826cb419 100644 --- a/tests/cli/config/test_show.py +++ b/tests/cli/config/test_show.py @@ -1,12 +1,12 @@ def test_default_scrubbed(hatch, config_file, helpers, default_cache_dir, default_data_dir): - config_file.model.project = 'foo' - config_file.model.publish['index']['auth'] = 'bar' + config_file.model.project = "foo" + config_file.model.publish["index"]["auth"] = "bar" config_file.save() - result = hatch('config', 'show') + result = hatch("config", "show") - default_cache_directory = str(default_cache_dir).replace('\\', '\\\\') - default_data_directory = str(default_data_dir).replace('\\', '\\\\') + default_cache_directory = str(default_cache_dir).replace("\\", "\\\\") + default_data_directory = str(default_data_dir).replace("\\", "\\\\") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -53,14 +53,14 @@ def test_default_scrubbed(hatch, config_file, helpers, default_cache_dir, defaul def test_reveal(hatch, config_file, helpers, default_cache_dir, default_data_dir): - config_file.model.project = 'foo' - config_file.model.publish['index']['auth'] = 'bar' + config_file.model.project = "foo" + config_file.model.publish["index"]["auth"] = "bar" config_file.save() - result = hatch('config', 'show', '-a') + result = hatch("config", "show", "-a") - default_cache_directory = str(default_cache_dir).replace('\\', '\\\\') - default_data_directory = str(default_data_dir).replace('\\', '\\\\') + default_cache_directory = str(default_cache_dir).replace("\\", "\\\\") + default_data_directory = str(default_data_dir).replace("\\", "\\\\") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( diff --git a/tests/cli/dep/show/test_requirements.py b/tests/cli/dep/show/test_requirements.py index 1471fdd15..201ebebdd 100644 --- a/tests/cli/dep/show/test_requirements.py +++ b/tests/cli/dep/show/test_requirements.py @@ -6,25 +6,25 @@ def test_incompatible_environment(hatch, temp_dir, helpers, build_env_config): - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() project = Project(path) config = dict(project.raw_config) - config['build-system']['requires'].append('foo') - config['project']['dynamic'].append('dependencies') + config["build-system"]["requires"].append("foo") + config["project"]["dynamic"].append("dependencies") project.save_config(config) - helpers.update_project_environment(project, 'hatch-build', {'python': '9000', **build_env_config}) + helpers.update_project_environment(project, "hatch-build", {"python": "9000", **build_env_config}) with path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('dep', 'show', 'requirements') + result = hatch("dep", "show", "requirements") assert result.exit_code == 1, result.output assert result.output == helpers.dedent( @@ -35,25 +35,25 @@ def test_incompatible_environment(hatch, temp_dir, helpers, build_env_config): def test_project_only(hatch, helpers, temp_dir, config_file): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' + project_path = temp_dir / "my-app" project = Project(project_path) config = dict(project.raw_config) - config['project']['dependencies'] = ['foo-bar-baz'] + config["project"]["dependencies"] = ["foo-bar-baz"] project.save_config(config) with project_path.as_cwd(): - result = hatch('dep', 'show', 'requirements', '-p') + result = hatch("dep", "show", "requirements", "-p") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -64,23 +64,23 @@ def test_project_only(hatch, helpers, temp_dir, config_file): def test_environment_only(hatch, helpers, temp_dir, config_file): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' + project_path = temp_dir / "my-app" project = Project(project_path) - helpers.update_project_environment(project, 'default', {'dependencies': ['foo-bar-baz']}) + helpers.update_project_environment(project, "default", {"dependencies": ["foo-bar-baz"]}) with project_path.as_cwd(): - result = hatch('dep', 'show', 'requirements', '-e') + result = hatch("dep", "show", "requirements", "-e") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -91,31 +91,31 @@ def test_environment_only(hatch, helpers, temp_dir, config_file): def test_default_both(hatch, helpers, temp_dir, config_file): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' + project_path = temp_dir / "my-app" project = Project(project_path) config = dict(project.raw_config) - config['project']['dependencies'] = ['foo-bar-baz'] - config['project']['optional-dependencies'] = { - 'feature1': ['bar-baz-foo'], - 'feature2': ['bar-foo-baz'], - 'feature3': ['foo-baz-bar'], + config["project"]["dependencies"] = ["foo-bar-baz"] + config["project"]["optional-dependencies"] = { + "feature1": ["bar-baz-foo"], + "feature2": ["bar-foo-baz"], + "feature3": ["foo-baz-bar"], } project.save_config(config) - helpers.update_project_environment(project, 'default', {'dependencies': ['baz-bar-foo']}) + helpers.update_project_environment(project, "default", {"dependencies": ["baz-bar-foo"]}) with project_path.as_cwd(): - result = hatch('dep', 'show', 'requirements') + result = hatch("dep", "show", "requirements") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -127,20 +127,20 @@ def test_default_both(hatch, helpers, temp_dir, config_file): def test_unknown_feature(hatch, helpers, temp_dir, config_file): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' + project_path = temp_dir / "my-app" with project_path.as_cwd(): - result = hatch('dep', 'show', 'requirements', '-f', 'foo') + result = hatch("dep", "show", "requirements", "-f", "foo") assert result.exit_code == 1, result.output assert result.output == helpers.dedent( @@ -151,32 +151,32 @@ def test_unknown_feature(hatch, helpers, temp_dir, config_file): def test_features_only(hatch, helpers, temp_dir, config_file): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' + project_path = temp_dir / "my-app" project = Project(project_path) config = dict(project.raw_config) - config['project']['dependencies'] = ['foo-bar-baz'] - config['project']['optional-dependencies'] = { - 'feature1': ['bar-baz-foo'], - 'feature2': ['bar-foo-baz'], - 'feature3': ['foo-baz-bar'], - 'feature4': ['baz-foo-bar'], + config["project"]["dependencies"] = ["foo-bar-baz"] + config["project"]["optional-dependencies"] = { + "feature1": ["bar-baz-foo"], + "feature2": ["bar-foo-baz"], + "feature3": ["foo-baz-bar"], + "feature4": ["baz-foo-bar"], } project.save_config(config) - helpers.update_project_environment(project, 'default', {'dependencies': ['baz-bar-foo']}) + helpers.update_project_environment(project, "default", {"dependencies": ["baz-bar-foo"]}) with project_path.as_cwd(): - result = hatch('dep', 'show', 'requirements', '-f', 'feature2', '-f', 'feature1') + result = hatch("dep", "show", "requirements", "-f", "feature2", "-f", "feature1") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -188,32 +188,32 @@ def test_features_only(hatch, helpers, temp_dir, config_file): def test_include_features(hatch, helpers, temp_dir, config_file): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' + project_path = temp_dir / "my-app" project = Project(project_path) config = dict(project.raw_config) - config['project']['dependencies'] = ['foo-bar-baz'] - config['project']['optional-dependencies'] = { - 'feature1': ['bar-baz-foo'], - 'feature2': ['bar-foo-baz'], - 'feature3': ['foo-baz-bar'], - 'feature4': ['baz-foo-bar'], + config["project"]["dependencies"] = ["foo-bar-baz"] + config["project"]["optional-dependencies"] = { + "feature1": ["bar-baz-foo"], + "feature2": ["bar-foo-baz"], + "feature3": ["foo-baz-bar"], + "feature4": ["baz-foo-bar"], } project.save_config(config) - helpers.update_project_environment(project, 'default', {'dependencies': ['baz-bar-foo']}) + helpers.update_project_environment(project, "default", {"dependencies": ["baz-bar-foo"]}) with project_path.as_cwd(): - result = hatch('dep', 'show', 'requirements', '--all') + result = hatch("dep", "show", "requirements", "--all") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -229,17 +229,17 @@ def test_include_features(hatch, helpers, temp_dir, config_file): def test_plugin_dependencies_unmet(hatch, helpers, temp_dir, config_file, mock_plugin_installation): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' + project_path = temp_dir / "my-app" dependency = os.urandom(16).hex() (project_path / DEFAULT_CONFIG_FILE).write_text( @@ -253,11 +253,11 @@ def test_plugin_dependencies_unmet(hatch, helpers, temp_dir, config_file, mock_p project = Project(project_path) config = dict(project.raw_config) - config['project']['dependencies'] = ['foo-bar-baz'] + config["project"]["dependencies"] = ["foo-bar-baz"] project.save_config(config) with project_path.as_cwd(): - result = hatch('dep', 'show', 'requirements', '-p') + result = hatch("dep", "show", "requirements", "-p") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( diff --git a/tests/cli/dep/show/test_table.py b/tests/cli/dep/show/test_table.py index 9748d1a16..9d4c5864f 100644 --- a/tests/cli/dep/show/test_table.py +++ b/tests/cli/dep/show/test_table.py @@ -8,32 +8,32 @@ from hatchling.utils.constants import DEFAULT_CONFIG_FILE -@pytest.fixture(scope='module', autouse=True) +@pytest.fixture(scope="module", autouse=True) def _terminal_width(): - with EnvVars({'COLUMNS': '200'}): + with EnvVars({"COLUMNS": "200"}): yield def test_incompatible_environment(hatch, temp_dir, helpers, build_env_config): - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() project = Project(path) config = dict(project.raw_config) - config['build-system']['requires'].append('foo') - config['project']['dynamic'].append('dependencies') + config["build-system"]["requires"].append("foo") + config["project"]["dynamic"].append("dependencies") project.save_config(config) - helpers.update_project_environment(project, 'hatch-build', {'python': '9000', **build_env_config}) + helpers.update_project_environment(project, "hatch-build", {"python": "9000", **build_env_config}) with path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('dep', 'show', 'table') + result = hatch("dep", "show", "table") assert result.exit_code == 1, result.output assert result.output == helpers.dedent( @@ -44,27 +44,27 @@ def test_incompatible_environment(hatch, temp_dir, helpers, build_env_config): def test_project_only(hatch, helpers, temp_dir, config_file): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() project = Project(project_path) config = dict(project.raw_config) - config['project']['dependencies'] = ['foo-bar-baz'] + config["project"]["dependencies"] = ["foo-bar-baz"] project.save_config(config) with project_path.as_cwd(): - result = hatch('dep', 'show', 'table', '--ascii', '-p') + result = hatch("dep", "show", "table", "--ascii", "-p") assert result.exit_code == 0, result.output assert helpers.remove_trailing_spaces(result.output) == helpers.dedent( @@ -80,25 +80,25 @@ def test_project_only(hatch, helpers, temp_dir, config_file): def test_environment_only(hatch, helpers, temp_dir, config_file): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() project = Project(project_path) - helpers.update_project_environment(project, 'default', {'dependencies': ['foo-bar-baz']}) + helpers.update_project_environment(project, "default", {"dependencies": ["foo-bar-baz"]}) with project_path.as_cwd(): - result = hatch('dep', 'show', 'table', '--ascii', '-e') + result = hatch("dep", "show", "table", "--ascii", "-e") assert result.exit_code == 0, result.output assert helpers.remove_trailing_spaces(result.output) == helpers.dedent( @@ -114,28 +114,28 @@ def test_environment_only(hatch, helpers, temp_dir, config_file): def test_default_both(hatch, helpers, temp_dir, config_file): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() project = Project(project_path) config = dict(project.raw_config) - config['project']['dependencies'] = ['foo-bar-baz'] + config["project"]["dependencies"] = ["foo-bar-baz"] project.save_config(config) - helpers.update_project_environment(project, 'default', {'dependencies': ['baz-bar-foo']}) + helpers.update_project_environment(project, "default", {"dependencies": ["baz-bar-foo"]}) with project_path.as_cwd(): - result = hatch('dep', 'show', 'table', '--ascii') + result = hatch("dep", "show", "table", "--ascii") assert result.exit_code == 0, result.output assert helpers.remove_trailing_spaces(result.output) == helpers.dedent( @@ -157,41 +157,41 @@ def test_default_both(hatch, helpers, temp_dir, config_file): def test_optional_columns(hatch, helpers, temp_dir, config_file): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() project = Project(project_path) config = dict(project.raw_config) - config['project']['dependencies'] = [ - 'python___dateutil', - 'bAr.Baz[TLS, EdDSA] >=1.2RC5', + config["project"]["dependencies"] = [ + "python___dateutil", + "bAr.Baz[TLS, EdDSA] >=1.2RC5", 'Foo;python_version<"3.8"', ] project.save_config(config) helpers.update_project_environment( project, - 'default', + "default", { - 'dependencies': [ - 'proj @ git+https://github.com/org/proj.git@v1', + "dependencies": [ + "proj @ git+https://github.com/org/proj.git@v1", 'bAr.Baz [TLS, EdDSA] >=1.2RC5;python_version<"3.8"', ], }, ) with project_path.as_cwd(): - result = hatch('dep', 'show', 'table', '--ascii') + result = hatch("dep", "show", "table", "--ascii") assert result.exit_code == 0, result.output assert helpers.remove_trailing_spaces(result.output) == helpers.dedent( @@ -216,18 +216,18 @@ def test_optional_columns(hatch, helpers, temp_dir, config_file): def test_plugin_dependencies_unmet(hatch, helpers, temp_dir, config_file, mock_plugin_installation): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() dependency = os.urandom(16).hex() @@ -242,11 +242,11 @@ def test_plugin_dependencies_unmet(hatch, helpers, temp_dir, config_file, mock_p project = Project(project_path) config = dict(project.raw_config) - config['project']['dependencies'] = ['foo-bar-baz'] + config["project"]["dependencies"] = ["foo-bar-baz"] project.save_config(config) with project_path.as_cwd(): - result = hatch('dep', 'show', 'table', '--ascii', '-p') + result = hatch("dep", "show", "table", "--ascii", "-p") assert result.exit_code == 0, result.output assert helpers.remove_trailing_spaces(result.output) == helpers.dedent( diff --git a/tests/cli/dep/test_hash.py b/tests/cli/dep/test_hash.py index 19904a391..348144c38 100644 --- a/tests/cli/dep/test_hash.py +++ b/tests/cli/dep/test_hash.py @@ -7,25 +7,25 @@ def test_incompatible_environment(hatch, temp_dir, helpers, build_env_config): - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() project = Project(path) config = dict(project.raw_config) - config['build-system']['requires'].append('foo') - config['project']['dynamic'].append('dependencies') + config["build-system"]["requires"].append("foo") + config["project"]["dynamic"].append("dependencies") project.save_config(config) - helpers.update_project_environment(project, 'hatch-build', {'python': '9000', **build_env_config}) + helpers.update_project_environment(project, "hatch-build", {"python": "9000", **build_env_config}) with path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('dep', 'hash') + result = hatch("dep", "hash") assert result.exit_code == 1, result.output assert result.output == helpers.dedent( @@ -36,24 +36,24 @@ def test_incompatible_environment(hatch, temp_dir, helpers, build_env_config): def test_all(hatch, helpers, temp_dir): - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' + project_path = temp_dir / "my-app" project = Project(project_path) config = dict(project.raw_config) - config['project']['dependencies'] = ['Foo', 'bar[ A, b]'] + config["project"]["dependencies"] = ["Foo", "bar[ A, b]"] project.save_config(config) - helpers.update_project_environment(project, 'default', {'dependencies': ['bAZ >= 0']}) - expected_hash = sha256(b'bar[a,b]baz>=0foo').hexdigest() + helpers.update_project_environment(project, "default", {"dependencies": ["bAZ >= 0"]}) + expected_hash = sha256(b"bar[a,b]baz>=0foo").hexdigest() with project_path.as_cwd(): - result = hatch('dep', 'hash') + result = hatch("dep", "hash") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -64,24 +64,24 @@ def test_all(hatch, helpers, temp_dir): def test_project_only(hatch, helpers, temp_dir): - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' + project_path = temp_dir / "my-app" project = Project(project_path) config = dict(project.raw_config) - config['project']['dependencies'] = ['Foo', 'bar[ A, b]'] + config["project"]["dependencies"] = ["Foo", "bar[ A, b]"] project.save_config(config) - helpers.update_project_environment(project, 'default', {'dependencies': ['bAZ >= 0']}) - expected_hash = sha256(b'bar[a,b]foo').hexdigest() + helpers.update_project_environment(project, "default", {"dependencies": ["bAZ >= 0"]}) + expected_hash = sha256(b"bar[a,b]foo").hexdigest() with project_path.as_cwd(): - result = hatch('dep', 'hash', '-p') + result = hatch("dep", "hash", "-p") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -92,14 +92,14 @@ def test_project_only(hatch, helpers, temp_dir): def test_plugin_dependencies_unmet(hatch, helpers, temp_dir, mock_plugin_installation): - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' + project_path = temp_dir / "my-app" dependency = os.urandom(16).hex() (project_path / DEFAULT_CONFIG_FILE).write_text( @@ -113,13 +113,13 @@ def test_plugin_dependencies_unmet(hatch, helpers, temp_dir, mock_plugin_install project = Project(project_path) config = dict(project.raw_config) - config['project']['dependencies'] = ['Foo', 'bar[ A, b]'] + config["project"]["dependencies"] = ["Foo", "bar[ A, b]"] project.save_config(config) - helpers.update_project_environment(project, 'default', {'dependencies': ['bAZ >= 0']}) - expected_hash = sha256(b'bar[a,b]foo').hexdigest() + helpers.update_project_environment(project, "default", {"dependencies": ["bAZ >= 0"]}) + expected_hash = sha256(b"bar[a,b]foo").hexdigest() with project_path.as_cwd(): - result = hatch('dep', 'hash', '-p') + result = hatch("dep", "hash", "-p") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( diff --git a/tests/cli/env/test_create.py b/tests/cli/env/test_create.py index 3c8b494bc..51fde0b76 100644 --- a/tests/cli/env/test_create.py +++ b/tests/cli/env/test_create.py @@ -13,22 +13,22 @@ def test_undefined(hatch, helpers, temp_dir, config_file): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('env', 'create', 'test') + result = hatch("env", "create", "test") assert result.exit_code == 1 assert result.output == helpers.dedent( @@ -39,28 +39,28 @@ def test_undefined(hatch, helpers, temp_dir, config_file): def test_unknown_type(hatch, helpers, temp_dir, config_file): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() project = Project(project_path) - config = dict(project.config.envs['default']) - config['type'] = 'foo' - helpers.update_project_environment(project, 'default', config) - helpers.update_project_environment(project, 'test', {}) + config = dict(project.config.envs["default"]) + config["type"] = "foo" + helpers.update_project_environment(project, "default", config) + helpers.update_project_environment(project, "test", {}) with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('env', 'create', 'test') + result = hatch("env", "create", "test") assert result.exit_code == 1 assert result.output == helpers.dedent( @@ -71,26 +71,26 @@ def test_unknown_type(hatch, helpers, temp_dir, config_file): def test_new(hatch, helpers, temp_dir, config_file): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() project = Project(project_path) - helpers.update_project_environment(project, 'default', {'skip-install': True, **project.config.envs['default']}) - helpers.update_project_environment(project, 'test', {}) + helpers.update_project_environment(project, "default", {"skip-install": True, **project.config.envs["default"]}) + helpers.update_project_environment(project, "test", {}) with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('env', 'create', 'test') + result = hatch("env", "create", "test") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -102,15 +102,15 @@ def test_new(hatch, helpers, temp_dir, config_file): project = Project(project_path) assert project.config.envs == { - 'default': {'type': 'virtual', 'skip-install': True}, - 'test': {'type': 'virtual', 'skip-install': True}, + "default": {"type": "virtual", "skip-install": True}, + "test": {"type": "virtual", "skip-install": True}, } - assert project.raw_config['tool']['hatch']['envs'] == { - 'default': {'type': 'virtual', 'skip-install': True}, - 'test': {}, + assert project.raw_config["tool"]["hatch"]["envs"] == { + "default": {"type": "virtual", "skip-install": True}, + "test": {}, } - env_data_path = data_path / 'env' / 'virtual' + env_data_path = data_path / "env" / "virtual" assert env_data_path.is_dir() project_data_path = env_data_path / project_path.name @@ -127,36 +127,37 @@ def test_new(hatch, helpers, temp_dir, config_file): env_path = env_dirs[0] - assert env_path.name == 'test' + assert env_path.name == "test" def test_uv_shipped(hatch, helpers, temp_dir, config_file): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() project = Project(project_path) helpers.update_project_environment( project, - 'default', - {'skip-install': True, 'installer': 'uv', **project.config.envs['default']}, + "default", + {"skip-install": True, "installer": "uv", **project.config.envs["default"]}, ) - helpers.update_project_environment(project, 'test', {}) + helpers.update_project_environment(project, "test", {}) - with project_path.as_cwd(), EnvVars( - {ConfigEnvVars.DATA: str(data_path)}, exclude=[get_env_var(plugin_name='virtual', option='uv_path')] + with ( + project_path.as_cwd(), + EnvVars({ConfigEnvVars.DATA: str(data_path)}, exclude=[get_env_var(plugin_name="virtual", option="uv_path")]), ): - result = hatch('env', 'create', 'test') + result = hatch("env", "create", "test") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -166,7 +167,7 @@ def test_uv_shipped(hatch, helpers, temp_dir, config_file): """ ) - env_data_path = data_path / 'env' / 'virtual' + env_data_path = data_path / "env" / "virtual" assert env_data_path.is_dir() project_data_path = env_data_path / project_path.name @@ -183,38 +184,39 @@ def test_uv_shipped(hatch, helpers, temp_dir, config_file): env_path = env_dirs[0] - assert env_path.name == 'test' + assert env_path.name == "test" @pytest.mark.requires_internet def test_uv_env(hatch, helpers, temp_dir, config_file): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() project = Project(project_path) helpers.update_project_environment( project, - 'default', - {'skip-install': True, 'installer': 'uv', **project.config.envs['default']}, + "default", + {"skip-install": True, "installer": "uv", **project.config.envs["default"]}, ) - helpers.update_project_environment(project, 'hatch-uv', {'dependencies': ['uv>=0.1.31']}) - helpers.update_project_environment(project, 'test', {}) + helpers.update_project_environment(project, "hatch-uv", {"dependencies": ["uv>=0.1.31"]}) + helpers.update_project_environment(project, "test", {}) - with project_path.as_cwd(), EnvVars( - {ConfigEnvVars.DATA: str(data_path)}, exclude=[get_env_var(plugin_name='virtual', option='uv_path')] + with ( + project_path.as_cwd(), + EnvVars({ConfigEnvVars.DATA: str(data_path)}, exclude=[get_env_var(plugin_name="virtual", option="uv_path")]), ): - result = hatch('env', 'create', 'test') + result = hatch("env", "create", "test") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -227,7 +229,7 @@ def test_uv_env(hatch, helpers, temp_dir, config_file): """ ) - env_data_path = data_path / 'env' / 'virtual' + env_data_path = data_path / "env" / "virtual" assert env_data_path.is_dir() project_data_path = env_data_path / project_path.name @@ -242,32 +244,32 @@ def test_uv_env(hatch, helpers, temp_dir, config_file): env_dirs = list(storage_path.iterdir()) assert len(env_dirs) == 2 - assert sorted(p.name for p in env_dirs) == ['hatch-uv', 'test'] + assert sorted(p.name for p in env_dirs) == ["hatch-uv", "test"] def test_new_selected_python(hatch, helpers, temp_dir, config_file, python_on_path, mocker): - mocker.patch('sys.executable') + mocker.patch("sys.executable") - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() project = Project(project_path) - helpers.update_project_environment(project, 'default', {'skip-install': True, **project.config.envs['default']}) - helpers.update_project_environment(project, 'test', {}) + helpers.update_project_environment(project, "default", {"skip-install": True, **project.config.envs["default"]}) + helpers.update_project_environment(project, "test", {}) with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path), AppEnvVars.PYTHON: python_on_path}): - result = hatch('env', 'create', 'test') + result = hatch("env", "create", "test") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -279,15 +281,15 @@ def test_new_selected_python(hatch, helpers, temp_dir, config_file, python_on_pa project = Project(project_path) assert project.config.envs == { - 'default': {'type': 'virtual', 'skip-install': True}, - 'test': {'type': 'virtual', 'skip-install': True}, + "default": {"type": "virtual", "skip-install": True}, + "test": {"type": "virtual", "skip-install": True}, } - assert project.raw_config['tool']['hatch']['envs'] == { - 'default': {'type': 'virtual', 'skip-install': True}, - 'test': {}, + assert project.raw_config["tool"]["hatch"]["envs"] == { + "default": {"type": "virtual", "skip-install": True}, + "test": {}, } - env_data_path = data_path / 'env' / 'virtual' + env_data_path = data_path / "env" / "virtual" assert env_data_path.is_dir() project_data_path = env_data_path / project_path.name @@ -304,31 +306,31 @@ def test_new_selected_python(hatch, helpers, temp_dir, config_file, python_on_pa env_path = env_dirs[0] - assert env_path.name == 'test' + assert env_path.name == "test" def test_selected_absolute_directory(hatch, helpers, temp_dir, config_file): - config_file.model.template.plugins['default']['tests'] = False - config_file.model.dirs.env = {'virtual': '$VENVS_DIR'} + config_file.model.template.plugins["default"]["tests"] = False + config_file.model.dirs.env = {"virtual": "$VENVS_DIR"} config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - env_data_path = temp_dir / '.venvs' + project_path = temp_dir / "my-app" + env_data_path = temp_dir / ".venvs" project = Project(project_path) - assert project.config.envs == {'default': {'type': 'virtual'}} - helpers.update_project_environment(project, 'default', {'skip-install': True, **project.config.envs['default']}) - helpers.update_project_environment(project, 'test', {}) + assert project.config.envs == {"default": {"type": "virtual"}} + helpers.update_project_environment(project, "default", {"skip-install": True, **project.config.envs["default"]}) + helpers.update_project_environment(project, "test", {}) - with project_path.as_cwd({'VENVS_DIR': str(env_data_path)}): - result = hatch('env', 'create', 'test') + with project_path.as_cwd({"VENVS_DIR": str(env_data_path)}): + result = hatch("env", "create", "test") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -340,12 +342,12 @@ def test_selected_absolute_directory(hatch, helpers, temp_dir, config_file): project = Project(project_path) assert project.config.envs == { - 'default': {'type': 'virtual', 'skip-install': True}, - 'test': {'type': 'virtual', 'skip-install': True}, + "default": {"type": "virtual", "skip-install": True}, + "test": {"type": "virtual", "skip-install": True}, } - assert project.raw_config['tool']['hatch']['envs'] == { - 'default': {'type': 'virtual', 'skip-install': True}, - 'test': {}, + assert project.raw_config["tool"]["hatch"]["envs"] == { + "default": {"type": "virtual", "skip-install": True}, + "test": {}, } assert env_data_path.is_dir() @@ -364,32 +366,32 @@ def test_selected_absolute_directory(hatch, helpers, temp_dir, config_file): env_path = env_dirs[0] - assert env_path.name == 'test' + assert env_path.name == "test" def test_option_absolute_directory(hatch, helpers, temp_dir, config_file): - config_file.model.template.plugins['default']['tests'] = False - config_file.model.dirs.env = {'virtual': '$VENVS_DIR'} + config_file.model.template.plugins["default"]["tests"] = False + config_file.model.dirs.env = {"virtual": "$VENVS_DIR"} config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - env_data_path = temp_dir / '.venvs' - env_path = temp_dir / 'foo' + project_path = temp_dir / "my-app" + env_data_path = temp_dir / ".venvs" + env_path = temp_dir / "foo" project = Project(project_path) - assert project.config.envs == {'default': {'type': 'virtual'}} - helpers.update_project_environment(project, 'default', {'skip-install': True, **project.config.envs['default']}) - helpers.update_project_environment(project, 'test', {'path': str(env_path)}) + assert project.config.envs == {"default": {"type": "virtual"}} + helpers.update_project_environment(project, "default", {"skip-install": True, **project.config.envs["default"]}) + helpers.update_project_environment(project, "test", {"path": str(env_path)}) - with project_path.as_cwd({'VENVS_DIR': str(env_data_path)}): - result = hatch('env', 'create', 'test') + with project_path.as_cwd({"VENVS_DIR": str(env_data_path)}): + result = hatch("env", "create", "test") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -401,12 +403,12 @@ def test_option_absolute_directory(hatch, helpers, temp_dir, config_file): project = Project(project_path) assert project.config.envs == { - 'default': {'type': 'virtual', 'skip-install': True}, - 'test': {'type': 'virtual', 'skip-install': True, 'path': str(env_path)}, + "default": {"type": "virtual", "skip-install": True}, + "test": {"type": "virtual", "skip-install": True, "path": str(env_path)}, } - assert project.raw_config['tool']['hatch']['envs'] == { - 'default': {'type': 'virtual', 'skip-install': True}, - 'test': {'path': str(env_path)}, + assert project.raw_config["tool"]["hatch"]["envs"] == { + "default": {"type": "virtual", "skip-install": True}, + "test": {"path": str(env_path)}, } assert not env_data_path.is_dir() @@ -414,29 +416,29 @@ def test_option_absolute_directory(hatch, helpers, temp_dir, config_file): def test_env_var_absolute_directory(hatch, helpers, temp_dir, config_file): - config_file.model.template.plugins['default']['tests'] = False - config_file.model.dirs.env = {'virtual': '$VENVS_DIR'} + config_file.model.template.plugins["default"]["tests"] = False + config_file.model.dirs.env = {"virtual": "$VENVS_DIR"} config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - env_data_path = temp_dir / '.venvs' - env_path = temp_dir / 'foo' - env_path_overridden = temp_dir / 'bar' + project_path = temp_dir / "my-app" + env_data_path = temp_dir / ".venvs" + env_path = temp_dir / "foo" + env_path_overridden = temp_dir / "bar" project = Project(project_path) - assert project.config.envs == {'default': {'type': 'virtual'}} - helpers.update_project_environment(project, 'default', {'skip-install': True, **project.config.envs['default']}) - helpers.update_project_environment(project, 'test', {'path': str(env_path_overridden)}) + assert project.config.envs == {"default": {"type": "virtual"}} + helpers.update_project_environment(project, "default", {"skip-install": True, **project.config.envs["default"]}) + helpers.update_project_environment(project, "test", {"path": str(env_path_overridden)}) - with project_path.as_cwd({'VENVS_DIR': str(env_data_path), 'HATCH_ENV_TYPE_VIRTUAL_PATH': str(env_path)}): - result = hatch('env', 'create', 'test') + with project_path.as_cwd({"VENVS_DIR": str(env_data_path), "HATCH_ENV_TYPE_VIRTUAL_PATH": str(env_path)}): + result = hatch("env", "create", "test") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -448,12 +450,12 @@ def test_env_var_absolute_directory(hatch, helpers, temp_dir, config_file): project = Project(project_path) assert project.config.envs == { - 'default': {'type': 'virtual', 'skip-install': True}, - 'test': {'type': 'virtual', 'skip-install': True, 'path': str(env_path_overridden)}, + "default": {"type": "virtual", "skip-install": True}, + "test": {"type": "virtual", "skip-install": True, "path": str(env_path_overridden)}, } - assert project.raw_config['tool']['hatch']['envs'] == { - 'default': {'type': 'virtual', 'skip-install': True}, - 'test': {'path': str(env_path_overridden)}, + assert project.raw_config["tool"]["hatch"]["envs"] == { + "default": {"type": "virtual", "skip-install": True}, + "test": {"path": str(env_path_overridden)}, } assert not env_data_path.is_dir() @@ -461,25 +463,25 @@ def test_env_var_absolute_directory(hatch, helpers, temp_dir, config_file): def test_selected_local_directory(hatch, helpers, temp_dir, config_file): - config_file.model.template.plugins['default']['tests'] = False - config_file.model.dirs.env = {'virtual': '$VENVS_DIR'} + config_file.model.template.plugins["default"]["tests"] = False + config_file.model.dirs.env = {"virtual": "$VENVS_DIR"} config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' + project_path = temp_dir / "my-app" project = Project(project_path) - helpers.update_project_environment(project, 'default', {'skip-install': True, **project.config.envs['default']}) - helpers.update_project_environment(project, 'test', {'matrix': [{'version': ['9000', '42']}]}) + helpers.update_project_environment(project, "default", {"skip-install": True, **project.config.envs["default"]}) + helpers.update_project_environment(project, "test", {"matrix": [{"version": ["9000", "42"]}]}) - with project_path.as_cwd({'VENVS_DIR': '.hatch'}): - result = hatch('env', 'create') + with project_path.as_cwd({"VENVS_DIR": ".hatch"}): + result = hatch("env", "create") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -489,8 +491,8 @@ def test_selected_local_directory(hatch, helpers, temp_dir, config_file): """ ) - with project_path.as_cwd({'VENVS_DIR': '.hatch'}): - result = hatch('env', 'create', 'test') + with project_path.as_cwd({"VENVS_DIR": ".hatch"}): + result = hatch("env", "create", "test") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -504,46 +506,46 @@ def test_selected_local_directory(hatch, helpers, temp_dir, config_file): project = Project(project_path) assert project.config.envs == { - 'default': {'type': 'virtual', 'skip-install': True}, - 'test.9000': {'type': 'virtual', 'skip-install': True}, - 'test.42': {'type': 'virtual', 'skip-install': True}, + "default": {"type": "virtual", "skip-install": True}, + "test.9000": {"type": "virtual", "skip-install": True}, + "test.42": {"type": "virtual", "skip-install": True}, } - assert project.raw_config['tool']['hatch']['envs'] == { - 'default': {'type': 'virtual', 'skip-install': True}, - 'test': {'matrix': [{'version': ['9000', '42']}]}, + assert project.raw_config["tool"]["hatch"]["envs"] == { + "default": {"type": "virtual", "skip-install": True}, + "test": {"matrix": [{"version": ["9000", "42"]}]}, } - env_data_path = project_path / '.hatch' + env_data_path = project_path / ".hatch" assert env_data_path.is_dir() env_dirs = list(env_data_path.iterdir()) assert len(env_dirs) == 4 - assert sorted(entry.name for entry in env_dirs) == ['.gitignore', 'my-app', 'test.42', 'test.9000'] + assert sorted(entry.name for entry in env_dirs) == [".gitignore", "my-app", "test.42", "test.9000"] def test_option_local_directory(hatch, helpers, temp_dir, config_file): - config_file.model.template.plugins['default']['tests'] = False - config_file.model.dirs.env = {'virtual': '$VENVS_DIR'} + config_file.model.template.plugins["default"]["tests"] = False + config_file.model.dirs.env = {"virtual": "$VENVS_DIR"} config_file.save() - project_name = 'My.App' - env_data_path = temp_dir / '.venvs' + project_name = "My.App" + env_data_path = temp_dir / ".venvs" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' + project_path = temp_dir / "my-app" project = Project(project_path) - assert project.config.envs == {'default': {'type': 'virtual'}} - helpers.update_project_environment(project, 'default', {'skip-install': True, **project.config.envs['default']}) - helpers.update_project_environment(project, 'test', {'path': '.venv'}) + assert project.config.envs == {"default": {"type": "virtual"}} + helpers.update_project_environment(project, "default", {"skip-install": True, **project.config.envs["default"]}) + helpers.update_project_environment(project, "test", {"path": ".venv"}) - with project_path.as_cwd({'VENVS_DIR': str(env_data_path)}): - result = hatch('env', 'create', 'test') + with project_path.as_cwd({"VENVS_DIR": str(env_data_path)}): + result = hatch("env", "create", "test") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -555,39 +557,39 @@ def test_option_local_directory(hatch, helpers, temp_dir, config_file): project = Project(project_path) assert project.config.envs == { - 'default': {'type': 'virtual', 'skip-install': True}, - 'test': {'type': 'virtual', 'skip-install': True, 'path': '.venv'}, + "default": {"type": "virtual", "skip-install": True}, + "test": {"type": "virtual", "skip-install": True, "path": ".venv"}, } - assert project.raw_config['tool']['hatch']['envs'] == { - 'default': {'type': 'virtual', 'skip-install': True}, - 'test': {'path': '.venv'}, + assert project.raw_config["tool"]["hatch"]["envs"] == { + "default": {"type": "virtual", "skip-install": True}, + "test": {"path": ".venv"}, } assert not env_data_path.is_dir() - assert (project_path / '.venv').is_dir() + assert (project_path / ".venv").is_dir() def test_env_var_local_directory(hatch, helpers, temp_dir, config_file): - config_file.model.template.plugins['default']['tests'] = False - config_file.model.dirs.env = {'virtual': '$VENVS_DIR'} + config_file.model.template.plugins["default"]["tests"] = False + config_file.model.dirs.env = {"virtual": "$VENVS_DIR"} config_file.save() - project_name = 'My.App' - env_data_path = temp_dir / '.venvs' + project_name = "My.App" + env_data_path = temp_dir / ".venvs" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' + project_path = temp_dir / "my-app" project = Project(project_path) - assert project.config.envs == {'default': {'type': 'virtual'}} - helpers.update_project_environment(project, 'default', {'skip-install': True, **project.config.envs['default']}) - helpers.update_project_environment(project, 'test', {'path': '.foo'}) + assert project.config.envs == {"default": {"type": "virtual"}} + helpers.update_project_environment(project, "default", {"skip-install": True, **project.config.envs["default"]}) + helpers.update_project_environment(project, "test", {"path": ".foo"}) - with project_path.as_cwd({'VENVS_DIR': str(env_data_path), 'HATCH_ENV_TYPE_VIRTUAL_PATH': '.venv'}): - result = hatch('env', 'create', 'test') + with project_path.as_cwd({"VENVS_DIR": str(env_data_path), "HATCH_ENV_TYPE_VIRTUAL_PATH": ".venv"}): + result = hatch("env", "create", "test") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -599,44 +601,44 @@ def test_env_var_local_directory(hatch, helpers, temp_dir, config_file): project = Project(project_path) assert project.config.envs == { - 'default': {'type': 'virtual', 'skip-install': True}, - 'test': {'type': 'virtual', 'skip-install': True, 'path': '.foo'}, + "default": {"type": "virtual", "skip-install": True}, + "test": {"type": "virtual", "skip-install": True, "path": ".foo"}, } - assert project.raw_config['tool']['hatch']['envs'] == { - 'default': {'type': 'virtual', 'skip-install': True}, - 'test': {'path': '.foo'}, + assert project.raw_config["tool"]["hatch"]["envs"] == { + "default": {"type": "virtual", "skip-install": True}, + "test": {"path": ".foo"}, } assert not env_data_path.is_dir() - assert (project_path / '.venv').is_dir() + assert (project_path / ".venv").is_dir() def test_enter_project_directory(hatch, config_file, helpers, temp_dir): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() - project = 'foo' - config_file.model.mode = 'project' + project = "foo" + config_file.model.mode = "project" config_file.model.project = project config_file.model.projects = {project: str(project_path)} config_file.save() project = Project(project_path) - helpers.update_project_environment(project, 'default', {'skip-install': True, **project.config.envs['default']}) - helpers.update_project_environment(project, 'test', {}) + helpers.update_project_environment(project, "default", {"skip-install": True, **project.config.envs["default"]}) + helpers.update_project_environment(project, "test", {}) with EnvVars({ConfigEnvVars.DATA: str(data_path)}): - result = hatch('env', 'create', 'test') + result = hatch("env", "create", "test") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -646,7 +648,7 @@ def test_enter_project_directory(hatch, config_file, helpers, temp_dir): """ ) - env_data_path = data_path / 'env' / 'virtual' + env_data_path = data_path / "env" / "virtual" assert env_data_path.is_dir() project_data_path = env_data_path / project_path.name @@ -663,30 +665,30 @@ def test_enter_project_directory(hatch, config_file, helpers, temp_dir): env_path = env_dirs[0] - assert env_path.name == 'test' + assert env_path.name == "test" def test_already_created(hatch, config_file, helpers, temp_dir): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() project = Project(project_path) - helpers.update_project_environment(project, 'default', {'skip-install': True, **project.config.envs['default']}) - helpers.update_project_environment(project, 'test', {}) + helpers.update_project_environment(project, "default", {"skip-install": True, **project.config.envs["default"]}) + helpers.update_project_environment(project, "test", {}) with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('env', 'create', 'test') + result = hatch("env", "create", "test") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -696,7 +698,7 @@ def test_already_created(hatch, config_file, helpers, temp_dir): """ ) - env_data_path = data_path / 'env' / 'virtual' + env_data_path = data_path / "env" / "virtual" assert env_data_path.is_dir() project_data_path = env_data_path / project_path.name @@ -713,10 +715,10 @@ def test_already_created(hatch, config_file, helpers, temp_dir): env_path = env_dirs[0] - assert env_path.name == 'test' + assert env_path.name == "test" with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('env', 'create', 'test') + result = hatch("env", "create", "test") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -727,25 +729,25 @@ def test_already_created(hatch, config_file, helpers, temp_dir): def test_default(hatch, config_file, helpers, temp_dir): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() project = Project(project_path) - helpers.update_project_environment(project, 'default', {'skip-install': True, **project.config.envs['default']}) + helpers.update_project_environment(project, "default", {"skip-install": True, **project.config.envs["default"]}) with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('env', 'create') + result = hatch("env", "create") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -755,7 +757,7 @@ def test_default(hatch, config_file, helpers, temp_dir): """ ) - env_data_path = data_path / 'env' / 'virtual' + env_data_path = data_path / "env" / "virtual" assert env_data_path.is_dir() project_data_path = env_data_path / project_path.name @@ -776,26 +778,26 @@ def test_default(hatch, config_file, helpers, temp_dir): def test_matrix(hatch, helpers, temp_dir, config_file): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() project = Project(project_path) - helpers.update_project_environment(project, 'default', {'skip-install': True, **project.config.envs['default']}) - helpers.update_project_environment(project, 'test', {'matrix': [{'version': ['9000', '42']}]}) + helpers.update_project_environment(project, "default", {"skip-install": True, **project.config.envs["default"]}) + helpers.update_project_environment(project, "test", {"matrix": [{"version": ["9000", "42"]}]}) with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('env', 'create', 'test') + result = hatch("env", "create", "test") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -809,16 +811,16 @@ def test_matrix(hatch, helpers, temp_dir, config_file): project = Project(project_path) assert project.config.envs == { - 'default': {'type': 'virtual', 'skip-install': True}, - 'test.9000': {'type': 'virtual', 'skip-install': True}, - 'test.42': {'type': 'virtual', 'skip-install': True}, + "default": {"type": "virtual", "skip-install": True}, + "test.9000": {"type": "virtual", "skip-install": True}, + "test.42": {"type": "virtual", "skip-install": True}, } - assert project.raw_config['tool']['hatch']['envs'] == { - 'default': {'type': 'virtual', 'skip-install': True}, - 'test': {'matrix': [{'version': ['9000', '42']}]}, + assert project.raw_config["tool"]["hatch"]["envs"] == { + "default": {"type": "virtual", "skip-install": True}, + "test": {"matrix": [{"version": ["9000", "42"]}]}, } - env_data_path = data_path / 'env' / 'virtual' + env_data_path = data_path / "env" / "virtual" assert env_data_path.is_dir() project_data_path = env_data_path / project_path.name @@ -833,33 +835,33 @@ def test_matrix(hatch, helpers, temp_dir, config_file): env_dirs = sorted(storage_path.iterdir(), key=lambda d: d.name) assert len(env_dirs) == 2 - assert env_dirs[0].name == 'test.42' - assert env_dirs[1].name == 'test.9000' + assert env_dirs[0].name == "test.42" + assert env_dirs[1].name == "test.9000" def test_incompatible_single(hatch, helpers, temp_dir, config_file): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() project = Project(project_path) helpers.update_project_environment( - project, 'default', {'skip-install': True, 'platforms': ['foo'], **project.config.envs['default']} + project, "default", {"skip-install": True, "platforms": ["foo"], **project.config.envs["default"]} ) - helpers.update_project_environment(project, 'test', {}) + helpers.update_project_environment(project, "test", {}) with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('env', 'create', 'test') + result = hatch("env", "create", "test") assert result.exit_code == 1 assert result.output == helpers.dedent( @@ -870,41 +872,41 @@ def test_incompatible_single(hatch, helpers, temp_dir, config_file): project = Project(project_path) assert project.config.envs == { - 'default': {'type': 'virtual', 'skip-install': True, 'platforms': ['foo']}, - 'test': {'type': 'virtual', 'skip-install': True, 'platforms': ['foo']}, + "default": {"type": "virtual", "skip-install": True, "platforms": ["foo"]}, + "test": {"type": "virtual", "skip-install": True, "platforms": ["foo"]}, } - assert project.raw_config['tool']['hatch']['envs'] == { - 'default': {'type': 'virtual', 'skip-install': True, 'platforms': ['foo']}, - 'test': {}, + assert project.raw_config["tool"]["hatch"]["envs"] == { + "default": {"type": "virtual", "skip-install": True, "platforms": ["foo"]}, + "test": {}, } - env_data_path = data_path / 'env' / 'virtual' + env_data_path = data_path / "env" / "virtual" assert not env_data_path.is_dir() def test_incompatible_matrix_full(hatch, helpers, temp_dir, config_file): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() project = Project(project_path) helpers.update_project_environment( - project, 'default', {'skip-install': True, 'platforms': ['foo'], **project.config.envs['default']} + project, "default", {"skip-install": True, "platforms": ["foo"], **project.config.envs["default"]} ) - helpers.update_project_environment(project, 'test', {'matrix': [{'version': ['9000', '42']}]}) + helpers.update_project_environment(project, "test", {"matrix": [{"version": ["9000", "42"]}]}) with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('env', 'create', 'test') + result = hatch("env", "create", "test") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -917,47 +919,47 @@ def test_incompatible_matrix_full(hatch, helpers, temp_dir, config_file): project = Project(project_path) assert project.config.envs == { - 'default': {'type': 'virtual', 'skip-install': True, 'platforms': ['foo']}, - 'test.9000': {'type': 'virtual', 'skip-install': True, 'platforms': ['foo']}, - 'test.42': {'type': 'virtual', 'skip-install': True, 'platforms': ['foo']}, + "default": {"type": "virtual", "skip-install": True, "platforms": ["foo"]}, + "test.9000": {"type": "virtual", "skip-install": True, "platforms": ["foo"]}, + "test.42": {"type": "virtual", "skip-install": True, "platforms": ["foo"]}, } - assert project.raw_config['tool']['hatch']['envs'] == { - 'default': {'type': 'virtual', 'skip-install': True, 'platforms': ['foo']}, - 'test': {'matrix': [{'version': ['9000', '42']}]}, + assert project.raw_config["tool"]["hatch"]["envs"] == { + "default": {"type": "virtual", "skip-install": True, "platforms": ["foo"]}, + "test": {"matrix": [{"version": ["9000", "42"]}]}, } - env_data_path = data_path / 'env' / 'virtual' + env_data_path = data_path / "env" / "virtual" assert not env_data_path.is_dir() def test_incompatible_matrix_partial(hatch, helpers, temp_dir, config_file): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() project = Project(project_path) - helpers.update_project_environment(project, 'default', {'skip-install': True, **project.config.envs['default']}) + helpers.update_project_environment(project, "default", {"skip-install": True, **project.config.envs["default"]}) helpers.update_project_environment( project, - 'test', + "test", { - 'matrix': [{'version': ['9000', '42']}], - 'overrides': {'matrix': {'version': {'platforms': [{'value': 'foo', 'if': ['9000']}]}}}, + "matrix": [{"version": ["9000", "42"]}], + "overrides": {"matrix": {"version": {"platforms": [{"value": "foo", "if": ["9000"]}]}}}, }, ) with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('env', 'create', 'test') + result = hatch("env", "create", "test") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -971,19 +973,19 @@ def test_incompatible_matrix_partial(hatch, helpers, temp_dir, config_file): project = Project(project_path) assert project.config.envs == { - 'default': {'type': 'virtual', 'skip-install': True}, - 'test.9000': {'type': 'virtual', 'skip-install': True, 'platforms': ['foo']}, - 'test.42': {'type': 'virtual', 'skip-install': True}, + "default": {"type": "virtual", "skip-install": True}, + "test.9000": {"type": "virtual", "skip-install": True, "platforms": ["foo"]}, + "test.42": {"type": "virtual", "skip-install": True}, } - assert project.raw_config['tool']['hatch']['envs'] == { - 'default': {'type': 'virtual', 'skip-install': True}, - 'test': { - 'matrix': [{'version': ['9000', '42']}], - 'overrides': {'matrix': {'version': {'platforms': [{'value': 'foo', 'if': ['9000']}]}}}, + assert project.raw_config["tool"]["hatch"]["envs"] == { + "default": {"type": "virtual", "skip-install": True}, + "test": { + "matrix": [{"version": ["9000", "42"]}], + "overrides": {"matrix": {"version": {"platforms": [{"value": "foo", "if": ["9000"]}]}}}, }, } - env_data_path = data_path / 'env' / 'virtual' + env_data_path = data_path / "env" / "virtual" assert env_data_path.is_dir() project_data_path = env_data_path / project_path.name @@ -998,29 +1000,29 @@ def test_incompatible_matrix_partial(hatch, helpers, temp_dir, config_file): env_dirs = list(storage_path.iterdir()) assert len(env_dirs) == 1 - assert env_dirs[0].name == 'test.42' + assert env_dirs[0].name == "test.42" @pytest.mark.requires_internet def test_install_project_default_dev_mode( hatch, helpers, temp_dir, platform, uv_on_path, extract_installed_requirements ): - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() project = Project(project_path) - helpers.update_project_environment(project, 'test', {}) + helpers.update_project_environment(project, "test", {}) with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('env', 'create', 'test') + result = hatch("env", "create", "test") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -1031,7 +1033,7 @@ def test_install_project_default_dev_mode( """ ) - env_data_path = data_path / 'env' / 'virtual' + env_data_path = data_path / "env" / "virtual" assert env_data_path.is_dir() project_data_path = env_data_path / project_path.name @@ -1048,37 +1050,37 @@ def test_install_project_default_dev_mode( env_path = env_dirs[0] - assert env_path.name == 'test' + assert env_path.name == "test" with UVVirtualEnv(env_path, platform): - output = platform.run_command([uv_on_path, 'pip', 'freeze'], check=True, capture_output=True).stdout.decode( - 'utf-8' + output = platform.run_command([uv_on_path, "pip", "freeze"], check=True, capture_output=True).stdout.decode( + "utf-8" ) requirements = extract_installed_requirements(output.splitlines()) assert len(requirements) == 1 - assert requirements[0].lower() == f'-e {project_path.as_uri().lower()}' + assert requirements[0].lower() == f"-e {project_path.as_uri().lower()}" @pytest.mark.requires_internet def test_install_project_no_dev_mode(hatch, helpers, temp_dir, platform, uv_on_path, extract_installed_requirements): - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() project = Project(project_path) - helpers.update_project_environment(project, 'default', {'dev-mode': False, **project.config.envs['default']}) - helpers.update_project_environment(project, 'test', {}) + helpers.update_project_environment(project, "default", {"dev-mode": False, **project.config.envs["default"]}) + helpers.update_project_environment(project, "test", {}) with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('env', 'create', 'test') + result = hatch("env", "create", "test") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -1089,7 +1091,7 @@ def test_install_project_no_dev_mode(hatch, helpers, temp_dir, platform, uv_on_p """ ) - env_data_path = data_path / 'env' / 'virtual' + env_data_path = data_path / "env" / "virtual" assert env_data_path.is_dir() project_data_path = env_data_path / project_path.name @@ -1106,47 +1108,47 @@ def test_install_project_no_dev_mode(hatch, helpers, temp_dir, platform, uv_on_p env_path = env_dirs[0] - assert env_path.name == 'test' + assert env_path.name == "test" with UVVirtualEnv(env_path, platform): - output = platform.run_command([uv_on_path, 'pip', 'freeze'], check=True, capture_output=True).stdout.decode( - 'utf-8' + output = platform.run_command([uv_on_path, "pip", "freeze"], check=True, capture_output=True).stdout.decode( + "utf-8" ) requirements = extract_installed_requirements(output.splitlines()) assert len(requirements) == 1 - assert requirements[0].lower() == f'my-app @ {project_path.as_uri().lower()}' + assert requirements[0].lower() == f"my-app @ {project_path.as_uri().lower()}" @pytest.mark.requires_internet def test_pre_install_commands(hatch, helpers, temp_dir, config_file): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() project = Project(project_path) helpers.update_project_environment( project, - 'default', + "default", { - 'pre-install-commands': ["python -c \"with open('test.txt', 'w') as f: f.write('content')\""], - **project.config.envs['default'], + "pre-install-commands": ["python -c \"with open('test.txt', 'w') as f: f.write('content')\""], + **project.config.envs["default"], }, ) - helpers.update_project_environment(project, 'test', {}) + helpers.update_project_environment(project, "test", {}) with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('env', 'create', 'test') + result = hatch("env", "create", "test") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -1157,34 +1159,34 @@ def test_pre_install_commands(hatch, helpers, temp_dir, config_file): Checking dependencies """ ) - assert (project_path / 'test.txt').is_file() + assert (project_path / "test.txt").is_file() def test_pre_install_commands_error(hatch, helpers, temp_dir, config_file): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() project = Project(project_path) helpers.update_project_environment( project, - 'default', - {'pre-install-commands': ['python -c "import sys;sys.exit(7)"'], **project.config.envs['default']}, + "default", + {"pre-install-commands": ['python -c "import sys;sys.exit(7)"'], **project.config.envs["default"]}, ) - helpers.update_project_environment(project, 'test', {}) + helpers.update_project_environment(project, "test", {}) with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('env', 'create', 'test') + result = hatch("env", "create", "test") assert result.exit_code == 7 assert result.output == helpers.dedent( @@ -1198,33 +1200,33 @@ def test_pre_install_commands_error(hatch, helpers, temp_dir, config_file): @pytest.mark.requires_internet def test_post_install_commands(hatch, helpers, temp_dir, config_file): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() project = Project(project_path) helpers.update_project_environment( project, - 'default', + "default", { - 'post-install-commands': ["python -c \"with open('test.txt', 'w') as f: f.write('content')\""], - **project.config.envs['default'], + "post-install-commands": ["python -c \"with open('test.txt', 'w') as f: f.write('content')\""], + **project.config.envs["default"], }, ) - helpers.update_project_environment(project, 'test', {}) + helpers.update_project_environment(project, "test", {}) with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('env', 'create', 'test') + result = hatch("env", "create", "test") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -1235,35 +1237,35 @@ def test_post_install_commands(hatch, helpers, temp_dir, config_file): Checking dependencies """ ) - assert (project_path / 'test.txt').is_file() + assert (project_path / "test.txt").is_file() @pytest.mark.requires_internet def test_post_install_commands_error(hatch, helpers, temp_dir, config_file): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() project = Project(project_path) helpers.update_project_environment( project, - 'default', - {'post-install-commands': ['python -c "import sys;sys.exit(7)"'], **project.config.envs['default']}, + "default", + {"post-install-commands": ['python -c "import sys;sys.exit(7)"'], **project.config.envs["default"]}, ) - helpers.update_project_environment(project, 'test', {}) + helpers.update_project_environment(project, "test", {}) with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('env', 'create', 'test') + result = hatch("env", "create", "test") assert result.exit_code == 7 assert result.output == helpers.dedent( @@ -1278,31 +1280,31 @@ def test_post_install_commands_error(hatch, helpers, temp_dir, config_file): @pytest.mark.requires_internet def test_sync_dependencies_uv(hatch, helpers, temp_dir, platform, uv_on_path, extract_installed_requirements): - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() project = Project(project_path) helpers.update_project_environment( project, - 'default', + "default", { - 'dependencies': ['binary'], - 'post-install-commands': ["python -c \"with open('test.txt', 'w') as f: f.write('content')\""], - **project.config.envs['default'], + "dependencies": ["binary"], + "post-install-commands": ["python -c \"with open('test.txt', 'w') as f: f.write('content')\""], + **project.config.envs["default"], }, ) - helpers.update_project_environment(project, 'test', {}) + helpers.update_project_environment(project, "test", {}) with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('env', 'create', 'test') + result = hatch("env", "create", "test") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -1314,9 +1316,9 @@ def test_sync_dependencies_uv(hatch, helpers, temp_dir, platform, uv_on_path, ex Syncing dependencies """ ) - assert (project_path / 'test.txt').is_file() + assert (project_path / "test.txt").is_file() - env_data_path = data_path / 'env' / 'virtual' + env_data_path = data_path / "env" / "virtual" assert env_data_path.is_dir() project_data_path = env_data_path / project_path.name @@ -1333,48 +1335,49 @@ def test_sync_dependencies_uv(hatch, helpers, temp_dir, platform, uv_on_path, ex env_path = env_dirs[0] - assert env_path.name == 'test' + assert env_path.name == "test" with UVVirtualEnv(env_path, platform): - output = platform.run_command([uv_on_path, 'pip', 'freeze'], check=True, capture_output=True).stdout.decode( - 'utf-8' + output = platform.run_command([uv_on_path, "pip", "freeze"], check=True, capture_output=True).stdout.decode( + "utf-8" ) requirements = extract_installed_requirements(output.splitlines()) assert len(requirements) == 2 - assert requirements[0].startswith('binary==') - assert requirements[1].lower() == f'-e {project_path.as_uri().lower()}' + assert requirements[0].startswith("binary==") + assert requirements[1].lower() == f"-e {project_path.as_uri().lower()}" @pytest.mark.requires_internet def test_sync_dependencies_pip(hatch, helpers, temp_dir, platform, extract_installed_requirements): - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() project = Project(project_path) helpers.update_project_environment( project, - 'default', + "default", { - 'dependencies': ['binary'], - 'post-install-commands': ["python -c \"with open('test.txt', 'w') as f: f.write('content')\""], - **project.config.envs['default'], + "dependencies": ["binary"], + "post-install-commands": ["python -c \"with open('test.txt', 'w') as f: f.write('content')\""], + **project.config.envs["default"], }, ) - helpers.update_project_environment(project, 'test', {}) + helpers.update_project_environment(project, "test", {}) - with project_path.as_cwd(), EnvVars( - {ConfigEnvVars.DATA: str(data_path)}, exclude=[get_env_var(plugin_name='virtual', option='uv_path')] + with ( + project_path.as_cwd(), + EnvVars({ConfigEnvVars.DATA: str(data_path)}, exclude=[get_env_var(plugin_name="virtual", option="uv_path")]), ): - result = hatch('env', 'create', 'test') + result = hatch("env", "create", "test") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -1386,9 +1389,9 @@ def test_sync_dependencies_pip(hatch, helpers, temp_dir, platform, extract_insta Syncing dependencies """ ) - assert (project_path / 'test.txt').is_file() + assert (project_path / "test.txt").is_file() - env_data_path = data_path / 'env' / 'virtual' + env_data_path = data_path / "env" / "virtual" assert env_data_path.is_dir() project_data_path = env_data_path / project_path.name @@ -1405,39 +1408,39 @@ def test_sync_dependencies_pip(hatch, helpers, temp_dir, platform, extract_insta env_path = env_dirs[0] - assert env_path.name == 'test' + assert env_path.name == "test" with VirtualEnv(env_path, platform): - output = platform.run_command(['pip', 'freeze'], check=True, capture_output=True).stdout.decode('utf-8') + output = platform.run_command(["pip", "freeze"], check=True, capture_output=True).stdout.decode("utf-8") requirements = extract_installed_requirements(output.splitlines()) assert len(requirements) == 2 - assert requirements[0].startswith('binary==') - assert requirements[1].lower() == f'-e {str(project_path).lower()}' + assert requirements[0].startswith("binary==") + assert requirements[1].lower() == f"-e {str(project_path).lower()}" @pytest.mark.requires_internet def test_features(hatch, helpers, temp_dir, platform, uv_on_path, extract_installed_requirements): - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() project = Project(project_path) config = dict(project.raw_config) - config['project']['optional-dependencies'] = {'foo': ['binary']} + config["project"]["optional-dependencies"] = {"foo": ["binary"]} project.save_config(config) - helpers.update_project_environment(project, 'default', {'features': ['foo'], **project.config.envs['default']}) - helpers.update_project_environment(project, 'test', {}) + helpers.update_project_environment(project, "default", {"features": ["foo"], **project.config.envs["default"]}) + helpers.update_project_environment(project, "test", {}) with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('env', 'create', 'test') + result = hatch("env", "create", "test") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -1448,7 +1451,7 @@ def test_features(hatch, helpers, temp_dir, platform, uv_on_path, extract_instal """ ) - env_data_path = data_path / 'env' / 'virtual' + env_data_path = data_path / "env" / "virtual" assert env_data_path.is_dir() project_data_path = env_data_path / project_path.name @@ -1465,55 +1468,55 @@ def test_features(hatch, helpers, temp_dir, platform, uv_on_path, extract_instal env_path = env_dirs[0] - assert env_path.name == 'test' + assert env_path.name == "test" with UVVirtualEnv(env_path, platform): - output = platform.run_command([uv_on_path, 'pip', 'freeze'], check=True, capture_output=True).stdout.decode( - 'utf-8' + output = platform.run_command([uv_on_path, "pip", "freeze"], check=True, capture_output=True).stdout.decode( + "utf-8" ) requirements = extract_installed_requirements(output.splitlines()) assert len(requirements) == 2 - assert requirements[0].startswith('binary==') - assert requirements[1].lower() == f'-e {project_path.as_uri().lower()}' + assert requirements[0].startswith("binary==") + assert requirements[1].lower() == f"-e {project_path.as_uri().lower()}" @pytest.mark.requires_internet def test_sync_dynamic_dependencies(hatch, helpers, temp_dir, platform, uv_on_path, extract_installed_requirements): - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output for i in range(2): with temp_dir.as_cwd(): - result = hatch('new', f'{project_name}{i}') + result = hatch("new", f"{project_name}{i}") assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() project = Project(project_path) config = dict(project.raw_config) - config['project'].pop('dependencies') - config['project']['dynamic'].extend(('dependencies', 'optional-dependencies')) - config['tool']['hatch']['metadata'] = {'allow-direct-references': True, 'hooks': {'custom': {}}} + config["project"].pop("dependencies") + config["project"]["dynamic"].extend(("dependencies", "optional-dependencies")) + config["tool"]["hatch"]["metadata"] = {"allow-direct-references": True, "hooks": {"custom": {}}} project.save_config(config) helpers.update_project_environment( project, - 'default', + "default", { - 'dependencies': ['my-app1 @ {root:uri}/../my-app1'], - 'features': ['foo'], - 'post-install-commands': ["python -c \"with open('test.txt', 'w') as f: f.write('content')\""], - **project.config.envs['default'], + "dependencies": ["my-app1 @ {root:uri}/../my-app1"], + "features": ["foo"], + "post-install-commands": ["python -c \"with open('test.txt', 'w') as f: f.write('content')\""], + **project.config.envs["default"], }, ) - helpers.update_project_environment(project, 'test', {}) + helpers.update_project_environment(project, "test", {}) build_script = project_path / DEFAULT_BUILD_SCRIPT build_script.write_text( @@ -1530,7 +1533,7 @@ def update(self, metadata): ) with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('env', 'create', 'test') + result = hatch("env", "create", "test") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -1547,9 +1550,9 @@ def update(self, metadata): Syncing dependencies """ ) - assert (project_path / 'test.txt').is_file() + assert (project_path / "test.txt").is_file() - env_data_path = data_path / 'env' / 'virtual' + env_data_path = data_path / "env" / "virtual" assert env_data_path.is_dir() project_data_path = env_data_path / project_path.name @@ -1562,60 +1565,60 @@ def update(self, metadata): assert len(storage_path.name) == 8 env_dirs = sorted(storage_path.iterdir()) - assert [d.name for d in env_dirs] == ['hatch-build', 'test'] + assert [d.name for d in env_dirs] == ["hatch-build", "test"] env_path = env_dirs[1] with UVVirtualEnv(env_path, platform): - output = platform.run_command([uv_on_path, 'pip', 'freeze'], check=True, capture_output=True).stdout.decode( - 'utf-8' + output = platform.run_command([uv_on_path, "pip", "freeze"], check=True, capture_output=True).stdout.decode( + "utf-8" ) requirements = extract_installed_requirements(output.splitlines()) assert len(requirements) == 4 - assert requirements[0].startswith('binary==') - assert requirements[1].lower() == f'-e {project_path.as_uri().lower()}' - assert requirements[2].lower() == f'my-app0 @ {project_path.parent.as_uri().lower()}/my-app0' - assert requirements[3].lower() == f'my-app1 @ {project_path.parent.as_uri().lower()}/my-app1' + assert requirements[0].startswith("binary==") + assert requirements[1].lower() == f"-e {project_path.as_uri().lower()}" + assert requirements[2].lower() == f"my-app0 @ {project_path.parent.as_uri().lower()}/my-app0" + assert requirements[3].lower() == f"my-app1 @ {project_path.parent.as_uri().lower()}/my-app1" @pytest.mark.requires_internet def test_unknown_dynamic_feature(hatch, helpers, temp_dir, config_file): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output with temp_dir.as_cwd(): - result = hatch('new', f'{project_name}1') + result = hatch("new", f"{project_name}1") assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() project = Project(project_path) config = dict(project.raw_config) - config['build-system']['requires'].append(f'my-app1 @ {path_to_uri(project_path).lower()}/../my-app1') - config['project']['dynamic'].append('optional-dependencies') - config['tool']['hatch']['metadata'] = {'hooks': {'custom': {}}} + config["build-system"]["requires"].append(f"my-app1 @ {path_to_uri(project_path).lower()}/../my-app1") + config["project"]["dynamic"].append("optional-dependencies") + config["tool"]["hatch"]["metadata"] = {"hooks": {"custom": {}}} project.save_config(config) helpers.update_project_environment( project, - 'default', + "default", { - 'features': ['foo'], - 'post-install-commands': ["python -c \"with open('test.txt', 'w') as f: f.write('content')\""], - **project.config.envs['default'], + "features": ["foo"], + "post-install-commands": ["python -c \"with open('test.txt', 'w') as f: f.write('content')\""], + **project.config.envs["default"], }, ) - helpers.update_project_environment(project, 'test', {}) + helpers.update_project_environment(project, "test", {}) build_script = project_path / DEFAULT_BUILD_SCRIPT build_script.write_text( @@ -1630,35 +1633,38 @@ def update(self, metadata): ) ) - with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}), pytest.raises( - ValueError, - match=( - 'Feature `foo` of field `tool.hatch.envs.test.features` is not defined in the dynamic ' - 'field `project.optional-dependencies`' + with ( + project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}), + pytest.raises( + ValueError, + match=( + "Feature `foo` of field `tool.hatch.envs.test.features` is not defined in the dynamic " + "field `project.optional-dependencies`" + ), ), ): - hatch('env', 'create', 'test') + hatch("env", "create", "test") def test_no_project_file(hatch, helpers, temp_dir, config_file): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() - (project_path / 'pyproject.toml').remove() + (project_path / "pyproject.toml").remove() with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('env', 'create') + result = hatch("env", "create") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -1668,7 +1674,7 @@ def test_no_project_file(hatch, helpers, temp_dir, config_file): """ ) - env_data_path = data_path / 'env' / 'virtual' + env_data_path = data_path / "env" / "virtual" assert env_data_path.is_dir() project_data_path = env_data_path / project_path.name @@ -1689,18 +1695,18 @@ def test_no_project_file(hatch, helpers, temp_dir, config_file): def test_plugin_dependencies_unmet(hatch, config_file, helpers, temp_dir, mock_plugin_installation): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() dependency = os.urandom(16).hex() @@ -1714,10 +1720,10 @@ def test_plugin_dependencies_unmet(hatch, config_file, helpers, temp_dir, mock_p ) project = Project(project_path) - helpers.update_project_environment(project, 'default', {'skip-install': True, **project.config.envs['default']}) + helpers.update_project_environment(project, "default", {"skip-install": True, **project.config.envs["default"]}) with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('env', 'create') + result = hatch("env", "create") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -1729,7 +1735,7 @@ def test_plugin_dependencies_unmet(hatch, config_file, helpers, temp_dir, mock_p ) helpers.assert_plugin_installation(mock_plugin_installation, [dependency]) - env_data_path = data_path / 'env' / 'virtual' + env_data_path = data_path / "env" / "virtual" assert env_data_path.is_dir() project_data_path = env_data_path / project_path.name @@ -1749,23 +1755,23 @@ def test_plugin_dependencies_unmet(hatch, config_file, helpers, temp_dir, mock_p assert env_path.name == project_path.name -@pytest.mark.usefixtures('mock_plugin_installation') +@pytest.mark.usefixtures("mock_plugin_installation") def test_plugin_dependencies_met(hatch, config_file, helpers, temp_dir): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() - dependency = 'hatch' + dependency = "hatch" (project_path / DEFAULT_CONFIG_FILE).write_text( helpers.dedent( f""" @@ -1776,10 +1782,10 @@ def test_plugin_dependencies_met(hatch, config_file, helpers, temp_dir): ) project = Project(project_path) - helpers.update_project_environment(project, 'default', {'skip-install': True, **project.config.envs['default']}) + helpers.update_project_environment(project, "default", {"skip-install": True, **project.config.envs["default"]}) with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('env', 'create') + result = hatch("env", "create") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -1789,7 +1795,7 @@ def test_plugin_dependencies_met(hatch, config_file, helpers, temp_dir): """ ) - env_data_path = data_path / 'env' / 'virtual' + env_data_path = data_path / "env" / "virtual" assert env_data_path.is_dir() project_data_path = env_data_path / project_path.name @@ -1809,23 +1815,23 @@ def test_plugin_dependencies_met(hatch, config_file, helpers, temp_dir): assert env_path.name == project_path.name -@pytest.mark.usefixtures('mock_plugin_installation') +@pytest.mark.usefixtures("mock_plugin_installation") def test_plugin_dependencies_met_as_app(hatch, config_file, helpers, temp_dir): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() - dependency = 'hatch' + dependency = "hatch" (project_path / DEFAULT_CONFIG_FILE).write_text( helpers.dedent( f""" @@ -1836,12 +1842,12 @@ def test_plugin_dependencies_met_as_app(hatch, config_file, helpers, temp_dir): ) project = Project(project_path) - helpers.update_project_environment(project, 'default', {'skip-install': True, **project.config.envs['default']}) + helpers.update_project_environment(project, "default", {"skip-install": True, **project.config.envs["default"]}) with project_path.as_cwd( - env_vars={ConfigEnvVars.DATA: str(data_path), 'PYAPP': sys.executable, 'PYAPP_COMMAND_NAME': 'self'} + env_vars={ConfigEnvVars.DATA: str(data_path), "PYAPP": sys.executable, "PYAPP_COMMAND_NAME": "self"} ): - result = hatch('env', 'create') + result = hatch("env", "create") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -1851,7 +1857,7 @@ def test_plugin_dependencies_met_as_app(hatch, config_file, helpers, temp_dir): """ ) - env_data_path = data_path / 'env' / 'virtual' + env_data_path = data_path / "env" / "virtual" assert env_data_path.is_dir() project_data_path = env_data_path / project_path.name @@ -1873,27 +1879,27 @@ def test_plugin_dependencies_met_as_app(hatch, config_file, helpers, temp_dir): @pytest.mark.requires_internet def test_no_compatible_python(hatch, helpers, temp_dir, config_file): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() project = Project(project_path) config = dict(project.raw_config) - config['project']['requires-python'] = '==9000' + config["project"]["requires-python"] = "==9000" project.save_config(config) with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('env', 'create') + result = hatch("env", "create") assert result.exit_code == 1, result.output assert result.output == helpers.dedent( @@ -1904,28 +1910,28 @@ def test_no_compatible_python(hatch, helpers, temp_dir, config_file): def test_no_compatible_python_ok_if_not_installed(hatch, helpers, temp_dir, config_file): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() project = Project(project_path) config = dict(project.raw_config) - config['project']['requires-python'] = '==9000' + config["project"]["requires-python"] = "==9000" project.save_config(config) - helpers.update_project_environment(project, 'default', {'skip-install': True, **project.config.envs['default']}) + helpers.update_project_environment(project, "default", {"skip-install": True, **project.config.envs["default"]}) with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('env', 'create') + result = hatch("env", "create") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -1935,7 +1941,7 @@ def test_no_compatible_python_ok_if_not_installed(hatch, helpers, temp_dir, conf """ ) - env_data_path = data_path / 'env' / 'virtual' + env_data_path = data_path / "env" / "virtual" assert env_data_path.is_dir() project_data_path = env_data_path / project_path.name diff --git a/tests/cli/env/test_find.py b/tests/cli/env/test_find.py index 250f56dc1..7277c7fad 100644 --- a/tests/cli/env/test_find.py +++ b/tests/cli/env/test_find.py @@ -8,20 +8,20 @@ def test_undefined(hatch, helpers, temp_dir, config_file): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' + project_path = temp_dir / "my-app" with project_path.as_cwd(): - result = hatch('env', 'find', 'test') + result = hatch("env", "find", "test") assert result.exit_code == 1 assert result.output == helpers.dedent( @@ -32,23 +32,23 @@ def test_undefined(hatch, helpers, temp_dir, config_file): def test_single(hatch, helpers, temp_dir_data, config_file): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir_data.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir_data / 'my-app' + project_path = temp_dir_data / "my-app" project = Project(project_path) - helpers.update_project_environment(project, 'default', {'skip-install': True, **project.config.envs['default']}) + helpers.update_project_environment(project, "default", {"skip-install": True, **project.config.envs["default"]}) with project_path.as_cwd(): - result = hatch('env', 'create') + result = hatch("env", "create") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -58,7 +58,7 @@ def test_single(hatch, helpers, temp_dir_data, config_file): """ ) - env_data_path = temp_dir_data / 'data' / 'env' / 'virtual' + env_data_path = temp_dir_data / "data" / "env" / "virtual" assert env_data_path.is_dir() project_data_path = env_data_path / project_path.name @@ -68,10 +68,10 @@ def test_single(hatch, helpers, temp_dir_data, config_file): assert len(storage_dirs) == 1 storage_path = storage_dirs[0] - env_path = storage_path / 'my-app' + env_path = storage_path / "my-app" with project_path.as_cwd(): - result = hatch('env', 'find') + result = hatch("env", "find") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -82,31 +82,31 @@ def test_single(hatch, helpers, temp_dir_data, config_file): def test_matrix(hatch, helpers, temp_dir_data, config_file): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir_data.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir_data / 'my-app' + project_path = temp_dir_data / "my-app" project = Project(project_path) - helpers.update_project_environment(project, 'default', {'skip-install': True, **project.config.envs['default']}) + helpers.update_project_environment(project, "default", {"skip-install": True, **project.config.envs["default"]}) helpers.update_project_environment( project, - 'test', + "test", { - 'matrix': [{'version': ['9000', '42']}], - 'overrides': {'matrix': {'version': {'platforms': [{'value': 'foo', 'if': ['9000']}]}}}, + "matrix": [{"version": ["9000", "42"]}], + "overrides": {"matrix": {"version": {"platforms": [{"value": "foo", "if": ["9000"]}]}}}, }, ) with project_path.as_cwd(): - result = hatch('env', 'create', 'test') + result = hatch("env", "create", "test") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -118,7 +118,7 @@ def test_matrix(hatch, helpers, temp_dir_data, config_file): """ ) - env_data_path = temp_dir_data / 'data' / 'env' / 'virtual' + env_data_path = temp_dir_data / "data" / "env" / "virtual" assert env_data_path.is_dir() project_data_path = env_data_path / project_path.name @@ -130,35 +130,35 @@ def test_matrix(hatch, helpers, temp_dir_data, config_file): storage_path = storage_dirs[0] with project_path.as_cwd(): - result = hatch('env', 'find', 'test') + result = hatch("env", "find", "test") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( f""" - {storage_path / 'test.9000'} - {storage_path / 'test.42'} + {storage_path / "test.9000"} + {storage_path / "test.42"} """ ) def test_plugin_dependencies_unmet(hatch, helpers, temp_dir_data, config_file, mock_plugin_installation): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir_data.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir_data / 'my-app' + project_path = temp_dir_data / "my-app" project = Project(project_path) - helpers.update_project_environment(project, 'default', {'skip-install': True, **project.config.envs['default']}) + helpers.update_project_environment(project, "default", {"skip-install": True, **project.config.envs["default"]}) with project_path.as_cwd(): - result = hatch('env', 'create') + result = hatch("env", "create") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -168,7 +168,7 @@ def test_plugin_dependencies_unmet(hatch, helpers, temp_dir_data, config_file, m """ ) - env_data_path = temp_dir_data / 'data' / 'env' / 'virtual' + env_data_path = temp_dir_data / "data" / "env" / "virtual" assert env_data_path.is_dir() project_data_path = env_data_path / project_path.name @@ -178,7 +178,7 @@ def test_plugin_dependencies_unmet(hatch, helpers, temp_dir_data, config_file, m assert len(storage_dirs) == 1 storage_path = storage_dirs[0] - env_path = storage_path / 'my-app' + env_path = storage_path / "my-app" dependency = os.urandom(16).hex() (project_path / DEFAULT_CONFIG_FILE).write_text( @@ -191,7 +191,7 @@ def test_plugin_dependencies_unmet(hatch, helpers, temp_dir_data, config_file, m ) with project_path.as_cwd(): - result = hatch('env', 'find') + result = hatch("env", "find") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -203,33 +203,33 @@ def test_plugin_dependencies_unmet(hatch, helpers, temp_dir_data, config_file, m helpers.assert_plugin_installation(mock_plugin_installation, [dependency]) -@pytest.mark.skipif(sys.platform not in {'win32', 'darwin'}, reason='Case insensitive file system required') +@pytest.mark.skipif(sys.platform not in {"win32", "darwin"}, reason="Case insensitive file system required") def test_case_sensitivity(hatch, temp_dir_data): from hatch.utils.fs import Path - project_name = 'My.App' + project_name = "My.App" with temp_dir_data.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir_data / 'my-app' + project_path = temp_dir_data / "my-app" with project_path.as_cwd(): - result = hatch('env', 'find') + result = hatch("env", "find") assert result.exit_code == 0, result.output path_default = result.output.strip() with Path(str(project_path).upper()).as_cwd(): - result = hatch('env', 'find') + result = hatch("env", "find") assert result.exit_code == 0, result.output path_upper = result.output.strip() with Path(str(project_path).lower()).as_cwd(): - result = hatch('env', 'find') + result = hatch("env", "find") assert result.exit_code == 0, result.output path_lower = result.output.strip() diff --git a/tests/cli/env/test_prune.py b/tests/cli/env/test_prune.py index 6270bc563..56206cd90 100644 --- a/tests/cli/env/test_prune.py +++ b/tests/cli/env/test_prune.py @@ -6,25 +6,25 @@ def test_unknown_type(hatch, helpers, temp_dir_data, config_file): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir_data.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir_data / 'my-app' + project_path = temp_dir_data / "my-app" project = Project(project_path) - config = dict(project.config.envs['default']) - config['type'] = 'foo' - helpers.update_project_environment(project, 'default', config) + config = dict(project.config.envs["default"]) + config["type"] = "foo" + helpers.update_project_environment(project, "default", config) with project_path.as_cwd(): - result = hatch('env', 'prune') + result = hatch("env", "prune") assert result.exit_code == 1 assert result.output == helpers.dedent( @@ -35,34 +35,34 @@ def test_unknown_type(hatch, helpers, temp_dir_data, config_file): def test_all(hatch, helpers, temp_dir_data, config_file): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir_data.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir_data / 'my-app' + project_path = temp_dir_data / "my-app" project = Project(project_path) - helpers.update_project_environment(project, 'default', {'skip-install': True, **project.config.envs['default']}) - helpers.update_project_environment(project, 'foo', {}) - helpers.update_project_environment(project, 'bar', {}) + helpers.update_project_environment(project, "default", {"skip-install": True, **project.config.envs["default"]}) + helpers.update_project_environment(project, "foo", {}) + helpers.update_project_environment(project, "bar", {}) with project_path.as_cwd(): - result = hatch('env', 'create', 'foo') + result = hatch("env", "create", "foo") assert result.exit_code == 0, result.output with project_path.as_cwd(): - result = hatch('env', 'create', 'bar') + result = hatch("env", "create", "bar") assert result.exit_code == 0, result.output - env_data_path = temp_dir_data / 'data' / 'env' / 'virtual' + env_data_path = temp_dir_data / "data" / "env" / "virtual" assert env_data_path.is_dir() project_data_path = env_data_path / project_path.name @@ -78,7 +78,7 @@ def test_all(hatch, helpers, temp_dir_data, config_file): assert len(env_dirs) == 2 with project_path.as_cwd(): - result = hatch('env', 'prune') + result = hatch("env", "prune") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -92,45 +92,45 @@ def test_all(hatch, helpers, temp_dir_data, config_file): def test_incompatible_ok(hatch, helpers, temp_dir_data, config_file): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir_data.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir_data / 'my-app' + project_path = temp_dir_data / "my-app" project = Project(project_path) helpers.update_project_environment( - project, 'default', {'skip-install': True, 'platforms': ['foo'], **project.config.envs['default']} + project, "default", {"skip-install": True, "platforms": ["foo"], **project.config.envs["default"]} ) with project_path.as_cwd(): - result = hatch('env', 'prune') + result = hatch("env", "prune") assert result.exit_code == 0, result.output assert not result.output def test_active(hatch, temp_dir_data, helpers, config_file): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir_data.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir_data / 'my-app' + project_path = temp_dir_data / "my-app" - with project_path.as_cwd(env_vars={AppEnvVars.ENV_ACTIVE: 'default'}): - result = hatch('env', 'prune') + with project_path.as_cwd(env_vars={AppEnvVars.ENV_ACTIVE: "default"}): + result = hatch("env", "prune") assert result.exit_code == 1 assert result.output == helpers.dedent( @@ -141,17 +141,17 @@ def test_active(hatch, temp_dir_data, helpers, config_file): def test_plugin_dependencies_unmet(hatch, helpers, temp_dir_data, config_file, mock_plugin_installation): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir_data.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir_data / 'my-app' + project_path = temp_dir_data / "my-app" dependency = os.urandom(16).hex() (project_path / DEFAULT_CONFIG_FILE).write_text( @@ -165,11 +165,11 @@ def test_plugin_dependencies_unmet(hatch, helpers, temp_dir_data, config_file, m project = Project(project_path) helpers.update_project_environment( - project, 'default', {'skip-install': True, 'platforms': ['foo'], **project.config.envs['default']} + project, "default", {"skip-install": True, "platforms": ["foo"], **project.config.envs["default"]} ) with project_path.as_cwd(): - result = hatch('env', 'prune') + result = hatch("env", "prune") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( diff --git a/tests/cli/env/test_remove.py b/tests/cli/env/test_remove.py index 8b3ab2308..8e2db4ef4 100644 --- a/tests/cli/env/test_remove.py +++ b/tests/cli/env/test_remove.py @@ -6,17 +6,17 @@ def test_unknown(hatch, temp_dir_data, helpers): - project_name = 'My.App' + project_name = "My.App" with temp_dir_data.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir_data / 'my-app' + project_path = temp_dir_data / "my-app" with project_path.as_cwd(): - result = hatch('env', 'remove', 'foo') + result = hatch("env", "remove", "foo") assert result.exit_code == 1, result.output assert result.output == helpers.dedent( @@ -27,51 +27,51 @@ def test_unknown(hatch, temp_dir_data, helpers): def test_nonexistent(hatch, temp_dir_data): - project_name = 'My.App' + project_name = "My.App" with temp_dir_data.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir_data / 'my-app' + project_path = temp_dir_data / "my-app" with project_path.as_cwd(): - result = hatch('env', 'remove', 'default') + result = hatch("env", "remove", "default") assert result.exit_code == 0, result.output assert not result.output def test_single(hatch, helpers, temp_dir_data, config_file): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir_data.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir_data / 'my-app' + project_path = temp_dir_data / "my-app" project = Project(project_path) - helpers.update_project_environment(project, 'default', {'skip-install': True, **project.config.envs['default']}) - helpers.update_project_environment(project, 'foo', {}) - helpers.update_project_environment(project, 'bar', {}) + helpers.update_project_environment(project, "default", {"skip-install": True, **project.config.envs["default"]}) + helpers.update_project_environment(project, "foo", {}) + helpers.update_project_environment(project, "bar", {}) with project_path.as_cwd(): - result = hatch('env', 'create', 'foo') + result = hatch("env", "create", "foo") assert result.exit_code == 0, result.output with project_path.as_cwd(): - result = hatch('env', 'create', 'bar') + result = hatch("env", "create", "bar") assert result.exit_code == 0, result.output - env_data_path = temp_dir_data / 'data' / 'env' / 'virtual' + env_data_path = temp_dir_data / "data" / "env" / "virtual" assert env_data_path.is_dir() project_data_path = env_data_path / project_path.name @@ -86,14 +86,14 @@ def test_single(hatch, helpers, temp_dir_data, config_file): env_dirs = list(storage_path.iterdir()) assert len(env_dirs) == 2 - foo_env_path = storage_path / 'foo' - bar_env_path = storage_path / 'bar' + foo_env_path = storage_path / "foo" + bar_env_path = storage_path / "bar" assert foo_env_path.is_dir() assert bar_env_path.is_dir() with project_path.as_cwd(): - result = hatch('env', 'remove', 'bar') + result = hatch("env", "remove", "bar") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -107,34 +107,34 @@ def test_single(hatch, helpers, temp_dir_data, config_file): def test_all(hatch, helpers, temp_dir_data, config_file): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir_data.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir_data / 'my-app' + project_path = temp_dir_data / "my-app" project = Project(project_path) - helpers.update_project_environment(project, 'default', {'skip-install': True, **project.config.envs['default']}) - helpers.update_project_environment(project, 'foo', {}) - helpers.update_project_environment(project, 'bar', {}) + helpers.update_project_environment(project, "default", {"skip-install": True, **project.config.envs["default"]}) + helpers.update_project_environment(project, "foo", {}) + helpers.update_project_environment(project, "bar", {}) with project_path.as_cwd(): - result = hatch('env', 'create', 'foo') + result = hatch("env", "create", "foo") assert result.exit_code == 0, result.output with project_path.as_cwd(): - result = hatch('env', 'create', 'bar') + result = hatch("env", "create", "bar") assert result.exit_code == 0, result.output - env_data_path = temp_dir_data / 'data' / 'env' / 'virtual' + env_data_path = temp_dir_data / "data" / "env" / "virtual" assert env_data_path.is_dir() project_data_path = env_data_path / project_path.name @@ -149,14 +149,14 @@ def test_all(hatch, helpers, temp_dir_data, config_file): env_dirs = list(storage_path.iterdir()) assert len(env_dirs) == 2 - foo_env_path = storage_path / 'foo' - bar_env_path = storage_path / 'bar' + foo_env_path = storage_path / "foo" + bar_env_path = storage_path / "bar" assert foo_env_path.is_dir() assert bar_env_path.is_dir() with project_path.as_cwd(): - result = hatch('env', 'remove', 'foo') + result = hatch("env", "remove", "foo") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -166,7 +166,7 @@ def test_all(hatch, helpers, temp_dir_data, config_file): ) with project_path.as_cwd(): - result = hatch('env', 'remove', 'bar') + result = hatch("env", "remove", "bar") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -179,28 +179,28 @@ def test_all(hatch, helpers, temp_dir_data, config_file): def test_matrix_all(hatch, helpers, temp_dir_data, config_file): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir_data.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir_data / 'my-app' + project_path = temp_dir_data / "my-app" project = Project(project_path) - helpers.update_project_environment(project, 'default', {'skip-install': True, **project.config.envs['default']}) - helpers.update_project_environment(project, 'foo', {'matrix': [{'version': ['9000', '42']}]}) + helpers.update_project_environment(project, "default", {"skip-install": True, **project.config.envs["default"]}) + helpers.update_project_environment(project, "foo", {"matrix": [{"version": ["9000", "42"]}]}) with project_path.as_cwd(): - result = hatch('env', 'create', 'foo') + result = hatch("env", "create", "foo") assert result.exit_code == 0, result.output - env_data_path = temp_dir_data / 'data' / 'env' / 'virtual' + env_data_path = temp_dir_data / "data" / "env" / "virtual" assert env_data_path.is_dir() project_data_path = env_data_path / project_path.name @@ -215,14 +215,14 @@ def test_matrix_all(hatch, helpers, temp_dir_data, config_file): env_dirs = list(storage_path.iterdir()) assert len(env_dirs) == 2 - foo_env_path = storage_path / 'foo.42' - bar_env_path = storage_path / 'foo.9000' + foo_env_path = storage_path / "foo.42" + bar_env_path = storage_path / "foo.9000" assert foo_env_path.is_dir() assert bar_env_path.is_dir() with project_path.as_cwd(): - result = hatch('env', 'remove', 'foo') + result = hatch("env", "remove", "foo") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -236,38 +236,38 @@ def test_matrix_all(hatch, helpers, temp_dir_data, config_file): def test_matrix_all_local_directory(hatch, helpers, temp_dir_data, config_file): - config_file.model.template.plugins['default']['tests'] = False - config_file.model.dirs.env = {'virtual': '.hatch'} + config_file.model.template.plugins["default"]["tests"] = False + config_file.model.dirs.env = {"virtual": ".hatch"} config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir_data.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir_data / 'my-app' + project_path = temp_dir_data / "my-app" project = Project(project_path) - helpers.update_project_environment(project, 'default', {'skip-install': True, **project.config.envs['default']}) - helpers.update_project_environment(project, 'foo', {'matrix': [{'version': ['9000', '42']}]}) + helpers.update_project_environment(project, "default", {"skip-install": True, **project.config.envs["default"]}) + helpers.update_project_environment(project, "foo", {"matrix": [{"version": ["9000", "42"]}]}) with project_path.as_cwd(): - result = hatch('env', 'create', 'foo') + result = hatch("env", "create", "foo") assert result.exit_code == 0, result.output - env_data_path = project_path / '.hatch' + env_data_path = project_path / ".hatch" assert env_data_path.is_dir() env_dirs = list(env_data_path.iterdir()) assert len(env_dirs) == 3 - assert sorted(entry.name for entry in env_dirs) == ['.gitignore', 'foo.42', 'foo.9000'] + assert sorted(entry.name for entry in env_dirs) == [".gitignore", "foo.42", "foo.9000"] with project_path.as_cwd(): - result = hatch('env', 'remove', 'foo') + result = hatch("env", "remove", "foo") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -281,39 +281,39 @@ def test_matrix_all_local_directory(hatch, helpers, temp_dir_data, config_file): def test_incompatible_ok(hatch, helpers, temp_dir_data): - project_name = 'My.App' + project_name = "My.App" with temp_dir_data.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir_data / 'my-app' + project_path = temp_dir_data / "my-app" project = Project(project_path) helpers.update_project_environment( - project, 'default', {'skip-install': True, 'platforms': ['foo'], **project.config.envs['default']} + project, "default", {"skip-install": True, "platforms": ["foo"], **project.config.envs["default"]} ) with project_path.as_cwd(): - result = hatch('env', 'remove') + result = hatch("env", "remove") assert result.exit_code == 0, result.output assert not result.output def test_active(hatch, temp_dir_data, helpers): - project_name = 'My.App' + project_name = "My.App" with temp_dir_data.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir_data / 'my-app' + project_path = temp_dir_data / "my-app" - with project_path.as_cwd(env_vars={AppEnvVars.ENV_ACTIVE: 'default'}): - result = hatch('env', 'remove') + with project_path.as_cwd(env_vars={AppEnvVars.ENV_ACTIVE: "default"}): + result = hatch("env", "remove") assert result.exit_code == 1 assert result.output == helpers.dedent( @@ -324,28 +324,28 @@ def test_active(hatch, temp_dir_data, helpers): def test_active_override(hatch, helpers, temp_dir_data, config_file): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir_data.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir_data / 'my-app' + project_path = temp_dir_data / "my-app" project = Project(project_path) - helpers.update_project_environment(project, 'default', {'skip-install': True, **project.config.envs['default']}) - helpers.update_project_environment(project, 'foo', {}) + helpers.update_project_environment(project, "default", {"skip-install": True, **project.config.envs["default"]}) + helpers.update_project_environment(project, "foo", {}) with project_path.as_cwd(): - result = hatch('env', 'create') + result = hatch("env", "create") assert result.exit_code == 0, result.output - env_data_path = temp_dir_data / 'data' / 'env' / 'virtual' + env_data_path = temp_dir_data / "data" / "env" / "virtual" assert env_data_path.is_dir() project_data_path = env_data_path / project_path.name @@ -360,10 +360,10 @@ def test_active_override(hatch, helpers, temp_dir_data, config_file): env_dirs = list(storage_path.iterdir()) assert len(env_dirs) == 1 - (storage_path / 'default').is_dir() + (storage_path / "default").is_dir() - with project_path.as_cwd(env_vars={AppEnvVars.ENV_ACTIVE: 'foo'}): - result = hatch('env', 'remove', 'default') + with project_path.as_cwd(env_vars={AppEnvVars.ENV_ACTIVE: "foo"}): + result = hatch("env", "remove", "default") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -376,34 +376,34 @@ def test_active_override(hatch, helpers, temp_dir_data, config_file): def test_plugin_dependencies_unmet(hatch, helpers, temp_dir_data, config_file, mock_plugin_installation): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir_data.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir_data / 'my-app' + project_path = temp_dir_data / "my-app" project = Project(project_path) - helpers.update_project_environment(project, 'default', {'skip-install': True, **project.config.envs['default']}) - helpers.update_project_environment(project, 'foo', {}) - helpers.update_project_environment(project, 'bar', {}) + helpers.update_project_environment(project, "default", {"skip-install": True, **project.config.envs["default"]}) + helpers.update_project_environment(project, "foo", {}) + helpers.update_project_environment(project, "bar", {}) with project_path.as_cwd(): - result = hatch('env', 'create', 'foo') + result = hatch("env", "create", "foo") assert result.exit_code == 0, result.output with project_path.as_cwd(): - result = hatch('env', 'create', 'bar') + result = hatch("env", "create", "bar") assert result.exit_code == 0, result.output - env_data_path = temp_dir_data / 'data' / 'env' / 'virtual' + env_data_path = temp_dir_data / "data" / "env" / "virtual" assert env_data_path.is_dir() project_data_path = env_data_path / project_path.name @@ -418,8 +418,8 @@ def test_plugin_dependencies_unmet(hatch, helpers, temp_dir_data, config_file, m env_dirs = list(storage_path.iterdir()) assert len(env_dirs) == 2 - foo_env_path = storage_path / 'foo' - bar_env_path = storage_path / 'bar' + foo_env_path = storage_path / "foo" + bar_env_path = storage_path / "bar" assert foo_env_path.is_dir() assert bar_env_path.is_dir() @@ -435,7 +435,7 @@ def test_plugin_dependencies_unmet(hatch, helpers, temp_dir_data, config_file, m ) with project_path.as_cwd(): - result = hatch('env', 'remove', 'bar') + result = hatch("env", "remove", "bar") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( diff --git a/tests/cli/env/test_run.py b/tests/cli/env/test_run.py index 4a9721e58..3a730d7f5 100644 --- a/tests/cli/env/test_run.py +++ b/tests/cli/env/test_run.py @@ -6,38 +6,38 @@ def test_filter_not_mapping(hatch, helpers, temp_dir, config_file): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() project = Project(project_path) helpers.update_project_environment( project, - 'default', + "default", { - 'skip-install': True, - 'scripts': { - 'error': [ + "skip-install": True, + "scripts": { + "error": [ 'python -c "import sys;sys.exit(3)"', - 'python -c "import pathlib,sys;pathlib.Path(\'test.txt\').write_text(sys.executable)"', + "python -c \"import pathlib,sys;pathlib.Path('test.txt').write_text(sys.executable)\"", ], }, - **project.config.envs['default'], + **project.config.envs["default"], }, ) with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('env', 'run', 'error', '--filter', '[]') + result = hatch("env", "run", "error", "--filter", "[]") assert result.exit_code == 1 assert result.output == helpers.dedent( @@ -48,42 +48,42 @@ def test_filter_not_mapping(hatch, helpers, temp_dir, config_file): def test_filter(hatch, helpers, temp_dir, config_file): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() project = Project(project_path) - helpers.update_project_environment(project, 'default', {'skip-install': True, **project.config.envs['default']}) + helpers.update_project_environment(project, "default", {"skip-install": True, **project.config.envs["default"]}) helpers.update_project_environment( project, - 'test', + "test", { - 'matrix': [{'version': ['9000', '42']}], - 'overrides': {'matrix': {'version': {'foo-bar-option': {'value': True, 'if': ['42']}}}}, + "matrix": [{"version": ["9000", "42"]}], + "overrides": {"matrix": {"version": {"foo-bar-option": {"value": True, "if": ["42"]}}}}, }, ) with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): result = hatch( - 'env', - 'run', - '--env', - 'test', - '--filter', + "env", + "run", + "--env", + "test", + "--filter", '{"foo-bar-option":true}', - '--', - 'python', - '-c', + "--", + "python", + "-c", "import os,sys;open('test.txt', 'a').write(sys.executable+os.linesep[-1])", ) @@ -95,10 +95,10 @@ def test_filter(hatch, helpers, temp_dir, config_file): Checking dependencies """ ) - output_file = project_path / 'test.txt' + output_file = project_path / "test.txt" assert output_file.is_file() - env_data_path = data_path / 'env' / 'virtual' + env_data_path = data_path / "env" / "virtual" assert env_data_path.is_dir() project_data_path = env_data_path / project_path.name @@ -114,47 +114,47 @@ def test_filter(hatch, helpers, temp_dir, config_file): assert len(env_dirs) == 1 env_path = env_dirs[0] - assert env_path.name == 'test.42' + assert env_path.name == "test.42" python_path = str(output_file.read_text()).strip() assert str(env_path) in python_path def test_force_continue(hatch, helpers, temp_dir, config_file): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() project = Project(project_path) helpers.update_project_environment( project, - 'default', + "default", { - 'skip-install': True, - 'scripts': { - 'error': [ + "skip-install": True, + "scripts": { + "error": [ 'python -c "import sys;sys.exit(2)"', '- python -c "import sys;sys.exit(3)"', 'python -c "import sys;sys.exit(1)"', - 'python -c "import pathlib,sys;pathlib.Path(\'test.txt\').write_text(sys.executable)"', + "python -c \"import pathlib,sys;pathlib.Path('test.txt').write_text(sys.executable)\"", ], }, - **project.config.envs['default'], + **project.config.envs["default"], }, ) with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('env', 'run', '--force-continue', '--', 'error') + result = hatch("env", "run", "--force-continue", "--", "error") assert result.exit_code == 2 assert result.output == helpers.dedent( @@ -167,10 +167,10 @@ def test_force_continue(hatch, helpers, temp_dir, config_file): cmd [4] | python -c "import pathlib,sys;pathlib.Path('test.txt').write_text(sys.executable)" """ ) - output_file = project_path / 'test.txt' + output_file = project_path / "test.txt" assert output_file.is_file() - env_data_path = data_path / 'env' / 'virtual' + env_data_path = data_path / "env" / "virtual" assert env_data_path.is_dir() project_data_path = env_data_path / project_path.name @@ -193,36 +193,36 @@ def test_force_continue(hatch, helpers, temp_dir, config_file): def test_ignore_compatibility(hatch, helpers, temp_dir, config_file): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() project = Project(project_path) helpers.update_project_environment( - project, 'default', {'skip-install': True, 'platforms': ['foo'], **project.config.envs['default']} + project, "default", {"skip-install": True, "platforms": ["foo"], **project.config.envs["default"]} ) - helpers.update_project_environment(project, 'test', {}) + helpers.update_project_environment(project, "test", {}) with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): result = hatch( - 'env', - 'run', - '--ignore-compat', - '--env', - 'test', - '--', - 'python', - '-c', + "env", + "run", + "--ignore-compat", + "--env", + "test", + "--", + "python", + "-c", "import os,sys;open('test.txt', 'a').write(sys.executable+os.linesep[-1])", ) @@ -233,26 +233,26 @@ def test_ignore_compatibility(hatch, helpers, temp_dir, config_file): test -> unsupported platform """ ) - output_file = project_path / 'test.txt' + output_file = project_path / "test.txt" assert not output_file.is_file() - env_data_path = data_path / 'env' / 'virtual' + env_data_path = data_path / "env" / "virtual" assert not env_data_path.is_dir() def test_plugin_dependencies_unmet(hatch, helpers, temp_dir, config_file, mock_plugin_installation): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() dependency = os.urandom(16).hex() @@ -266,11 +266,11 @@ def test_plugin_dependencies_unmet(hatch, helpers, temp_dir, config_file, mock_p ) project = Project(project_path) - helpers.update_project_environment(project, 'default', {'skip-install': True, **project.config.envs['default']}) + helpers.update_project_environment(project, "default", {"skip-install": True, **project.config.envs["default"]}) with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): result = hatch( - 'env', 'run', '--', 'python', '-c', "import pathlib,sys;pathlib.Path('test.txt').write_text(sys.executable)" + "env", "run", "--", "python", "-c", "import pathlib,sys;pathlib.Path('test.txt').write_text(sys.executable)" ) assert result.exit_code == 0, result.output @@ -283,10 +283,10 @@ def test_plugin_dependencies_unmet(hatch, helpers, temp_dir, config_file, mock_p ) helpers.assert_plugin_installation(mock_plugin_installation, [dependency]) - output_file = project_path / 'test.txt' + output_file = project_path / "test.txt" assert output_file.is_file() - env_data_path = data_path / 'env' / 'virtual' + env_data_path = data_path / "env" / "virtual" assert env_data_path.is_dir() project_data_path = env_data_path / project_path.name diff --git a/tests/cli/env/test_show.py b/tests/cli/env/test_show.py index 09edc8286..c43dee7bd 100644 --- a/tests/cli/env/test_show.py +++ b/tests/cli/env/test_show.py @@ -9,29 +9,29 @@ from hatchling.utils.constants import DEFAULT_CONFIG_FILE -@pytest.fixture(scope='module', autouse=True) +@pytest.fixture(scope="module", autouse=True) def _terminal_width(): - with EnvVars({'COLUMNS': '200'}, exclude=[get_env_var(plugin_name='virtual', option='uv_path')]): + with EnvVars({"COLUMNS": "200"}, exclude=[get_env_var(plugin_name="virtual", option="uv_path")]): yield def test_default(hatch, helpers, temp_dir, config_file): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() with project_path.as_cwd(): - result = hatch('env', 'show', '--ascii') + result = hatch("env", "show", "--ascii") assert result.exit_code == 0, result.output assert helpers.remove_trailing_spaces(result.output) == helpers.dedent( @@ -47,62 +47,62 @@ def test_default(hatch, helpers, temp_dir, config_file): def test_default_as_json(hatch, temp_dir, config_file): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() with project_path.as_cwd(): - result = hatch('env', 'show', '--json') + result = hatch("env", "show", "--json") assert result.exit_code == 0, result.output environments = json.loads(result.output) assert list(environments) == [ - 'default', - 'hatch-build', - 'hatch-static-analysis', - 'hatch-test.py3.13', - 'hatch-test.py3.12', - 'hatch-test.py3.11', - 'hatch-test.py3.10', - 'hatch-test.py3.9', - 'hatch-test.py3.8', - 'hatch-uv', + "default", + "hatch-build", + "hatch-static-analysis", + "hatch-test.py3.13", + "hatch-test.py3.12", + "hatch-test.py3.11", + "hatch-test.py3.10", + "hatch-test.py3.9", + "hatch-test.py3.8", + "hatch-uv", ] - assert environments['default'] == {'type': 'virtual'} + assert environments["default"] == {"type": "virtual"} def test_single_only(hatch, helpers, temp_dir, config_file): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() project = Project(project_path) - helpers.update_project_environment(project, 'foo', {}) - helpers.update_project_environment(project, 'bar', {}) + helpers.update_project_environment(project, "foo", {}) + helpers.update_project_environment(project, "bar", {}) with project_path.as_cwd(): - result = hatch('env', 'show', '--ascii') + result = hatch("env", "show", "--ascii") assert result.exit_code == 0, result.output assert helpers.remove_trailing_spaces(result.output) == helpers.dedent( @@ -122,25 +122,25 @@ def test_single_only(hatch, helpers, temp_dir, config_file): def test_single_and_matrix(hatch, helpers, temp_dir, config_file): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() project = Project(project_path) - helpers.update_project_environment(project, 'foo', {'matrix': [{'version': ['9000', '3.14'], 'py': ['39', '310']}]}) + helpers.update_project_environment(project, "foo", {"matrix": [{"version": ["9000", "3.14"], "py": ["39", "310"]}]}) with project_path.as_cwd(): - result = hatch('env', 'show', '--ascii') + result = hatch("env", "show", "--ascii") assert result.exit_code == 0, result.output assert helpers.remove_trailing_spaces(result.output) == helpers.dedent( @@ -165,27 +165,27 @@ def test_single_and_matrix(hatch, helpers, temp_dir, config_file): def test_default_matrix_only(hatch, helpers, temp_dir, config_file): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() project = Project(project_path) helpers.update_project_environment( - project, 'default', {'matrix': [{'version': ['9000', '3.14'], 'py': ['39', '310']}]} + project, "default", {"matrix": [{"version": ["9000", "3.14"], "py": ["39", "310"]}]} ) with project_path.as_cwd(): - result = hatch('env', 'show', '--ascii') + result = hatch("env", "show", "--ascii") assert result.exit_code == 0, result.output assert helpers.remove_trailing_spaces(result.output) == helpers.dedent( @@ -204,29 +204,29 @@ def test_default_matrix_only(hatch, helpers, temp_dir, config_file): def test_all_matrix_types_with_single(hatch, helpers, temp_dir, config_file): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() project = Project(project_path) helpers.update_project_environment( - project, 'default', {'matrix': [{'version': ['9000', '3.14'], 'py': ['39', '310']}]} + project, "default", {"matrix": [{"version": ["9000", "3.14"], "py": ["39", "310"]}]} ) - helpers.update_project_environment(project, 'foo', {'matrix': [{'version': ['9000', '3.14'], 'py': ['39', '310']}]}) - helpers.update_project_environment(project, 'bar', {}) + helpers.update_project_environment(project, "foo", {"matrix": [{"version": ["9000", "3.14"], "py": ["39", "310"]}]}) + helpers.update_project_environment(project, "bar", {}) with project_path.as_cwd(): - result = hatch('env', 'show', '--ascii') + result = hatch("env", "show", "--ascii") assert result.exit_code == 0, result.output assert helpers.remove_trailing_spaces(result.output) == helpers.dedent( @@ -256,26 +256,26 @@ def test_all_matrix_types_with_single(hatch, helpers, temp_dir, config_file): def test_specific(hatch, helpers, temp_dir, config_file): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() project = Project(project_path) - helpers.update_project_environment(project, 'foo', {}) - helpers.update_project_environment(project, 'bar', {}) + helpers.update_project_environment(project, "foo", {}) + helpers.update_project_environment(project, "bar", {}) with project_path.as_cwd(): - result = hatch('env', 'show', 'bar', 'foo', '--ascii') + result = hatch("env", "show", "bar", "foo", "--ascii") assert result.exit_code == 0, result.output assert helpers.remove_trailing_spaces(result.output) == helpers.dedent( @@ -293,22 +293,22 @@ def test_specific(hatch, helpers, temp_dir, config_file): def test_specific_unknown(hatch, helpers, temp_dir, config_file): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() with project_path.as_cwd(): - result = hatch('env', 'show', 'foo', '--ascii') + result = hatch("env", "show", "foo", "--ascii") assert result.exit_code == 1, result.output assert helpers.remove_trailing_spaces(result.output) == helpers.dedent( @@ -319,23 +319,23 @@ def test_specific_unknown(hatch, helpers, temp_dir, config_file): def test_optional_columns(hatch, helpers, temp_dir, config_file): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() - dependencies = ['python___dateutil', 'bAr.Baz[TLS] >=1.2RC5'] + dependencies = ["python___dateutil", "bAr.Baz[TLS] >=1.2RC5"] extra_dependencies = ['Foo;python_version<"3.8"'] - env_vars = {'FOO': '1', 'BAR': '2'} + env_vars = {"FOO": "1", "BAR": "2"} description = """ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna \ aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. \ @@ -345,34 +345,34 @@ def test_optional_columns(hatch, helpers, temp_dir, config_file): project = Project(project_path) config = dict(project.raw_config) - config['project']['optional-dependencies'] = {'foo_bar': [], 'baz': []} + config["project"]["optional-dependencies"] = {"foo_bar": [], "baz": []} project.save_config(config) helpers.update_project_environment( project, - 'default', + "default", { - 'matrix': [{'version': ['9000', '3.14'], 'py': ['39', '310']}], - 'description': description, - 'dependencies': dependencies, - 'extra-dependencies': extra_dependencies, - 'env-vars': env_vars, - 'features': ['Foo...Bar', 'Baz', 'baZ'], - 'scripts': {'test': 'pytest', 'build': 'python -m build', '_foo': 'test'}, + "matrix": [{"version": ["9000", "3.14"], "py": ["39", "310"]}], + "description": description, + "dependencies": dependencies, + "extra-dependencies": extra_dependencies, + "env-vars": env_vars, + "features": ["Foo...Bar", "Baz", "baZ"], + "scripts": {"test": "pytest", "build": "python -m build", "_foo": "test"}, }, ) helpers.update_project_environment( project, - 'foo', + "foo", { - 'description': description, - 'dependencies': dependencies, - 'extra-dependencies': extra_dependencies, - 'env-vars': env_vars, + "description": description, + "dependencies": dependencies, + "extra-dependencies": extra_dependencies, + "env-vars": env_vars, }, ) with project_path.as_cwd(): - result = hatch('env', 'show', '--ascii') + result = hatch("env", "show", "--ascii") assert result.exit_code == 0, result.output assert helpers.remove_trailing_spaces(result.output) == helpers.dedent( @@ -403,18 +403,18 @@ def test_optional_columns(hatch, helpers, temp_dir, config_file): def test_context_formatting(hatch, helpers, temp_dir, config_file): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() project = Project(project_path) @@ -422,25 +422,25 @@ def test_context_formatting(hatch, helpers, temp_dir, config_file): # Without context formatting helpers.update_project_environment( project, - 'default', + "default", { - 'matrix': [{'version': ['9000', '3.14'], 'py': ['39', '310']}], - 'dependencies': ['foo @ {root:uri}/../foo'], + "matrix": [{"version": ["9000", "3.14"], "py": ["39", "310"]}], + "dependencies": ["foo @ {root:uri}/../foo"], }, ) # With context formatting helpers.update_project_environment( project, - 'foo', + "foo", { - 'env-vars': {'BAR': '{env:FOO_BAZ}'}, - 'dependencies': ['pydantic'], + "env-vars": {"BAR": "{env:FOO_BAZ}"}, + "dependencies": ["pydantic"], }, ) - with project_path.as_cwd(env_vars={'FOO_BAZ': 'FOO_BAR'}): - result = hatch('env', 'show', '--ascii') + with project_path.as_cwd(env_vars={"FOO_BAZ": "FOO_BAR"}): + result = hatch("env", "show", "--ascii") assert result.exit_code == 0, result.output assert helpers.remove_trailing_spaces(result.output) == helpers.dedent( @@ -465,18 +465,18 @@ def test_context_formatting(hatch, helpers, temp_dir, config_file): def test_plugin_dependencies_unmet(hatch, helpers, temp_dir, config_file, mock_plugin_installation): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() dependency = os.urandom(16).hex() @@ -490,7 +490,7 @@ def test_plugin_dependencies_unmet(hatch, helpers, temp_dir, config_file, mock_p ) with project_path.as_cwd(): - result = hatch('env', 'show', '--ascii') + result = hatch("env", "show", "--ascii") assert result.exit_code == 0, result.output assert helpers.remove_trailing_spaces(result.output) == helpers.dedent( diff --git a/tests/cli/fmt/test_fmt.py b/tests/cli/fmt/test_fmt.py index b3450507d..82ed528b2 100644 --- a/tests/cli/fmt/test_fmt.py +++ b/tests/cli/fmt/test_fmt.py @@ -10,55 +10,55 @@ def construct_ruff_defaults_file(rules: tuple[str, ...]) -> str: from hatch.cli.fmt.core import PER_FILE_IGNORED_RULES lines = [ - 'line-length = 120', - '', - '[format]', - 'docstring-code-format = true', - 'docstring-code-line-length = 80', - '', - '[lint]', + "line-length = 120", + "", + "[format]", + "docstring-code-format = true", + "docstring-code-line-length = 80", + "", + "[lint]", ] # Selected rules - lines.append('select = [') + lines.append("select = [") lines.extend(f' "{rule}",' for rule in sorted(rules)) - lines.extend((']', '')) + lines.extend(("]", "")) # Ignored rules - lines.append('[lint.per-file-ignores]') + lines.append("[lint.per-file-ignores]") for glob, ignored_rules in PER_FILE_IGNORED_RULES.items(): lines.append(f'"{glob}" = [') lines.extend(f' "{ignored_rule}",' for ignored_rule in ignored_rules) - lines.append(']') + lines.append("]") # Default config lines.extend(( - '', - '[lint.flake8-tidy-imports]', + "", + "[lint.flake8-tidy-imports]", 'ban-relative-imports = "all"', - '', - '[lint.isort]', + "", + "[lint.isort]", 'known-first-party = ["my_app"]', - '', - '[lint.flake8-pytest-style]', - 'fixture-parentheses = false', - 'mark-parentheses = false', + "", + "[lint.flake8-pytest-style]", + "fixture-parentheses = false", + "mark-parentheses = false", )) # Ensure the file ends with a newline to satisfy other linters - lines.append('') + lines.append("") - return '\n'.join(lines) + return "\n".join(lines) -@pytest.fixture(scope='module') +@pytest.fixture(scope="module") def defaults_file_stable() -> str: from hatch.cli.fmt.core import STABLE_RULES return construct_ruff_defaults_file(STABLE_RULES) -@pytest.fixture(scope='module') +@pytest.fixture(scope="module") def defaults_file_preview() -> str: from hatch.cli.fmt.core import PREVIEW_RULES, STABLE_RULES @@ -67,27 +67,27 @@ def defaults_file_preview() -> str: class TestDefaults: def test_fix(self, hatch, helpers, temp_dir, config_file, env_run, mocker, platform, defaults_file_stable): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() - config_dir = data_path / 'env' / '.internal' / 'hatch-static-analysis' / '.config' / project_path.id - default_config = config_dir / 'ruff_defaults.toml' - user_config = config_dir / 'pyproject.toml' + config_dir = data_path / "env" / ".internal" / "hatch-static-analysis" / ".config" / project_path.id + default_config = config_dir / "ruff_defaults.toml" + user_config = config_dir / "pyproject.toml" user_config_path = platform.join_command_args([str(user_config)]) with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('fmt') + result = hatch("fmt") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -98,14 +98,14 @@ def test_fix(self, hatch, helpers, temp_dir, config_file, env_run, mocker, platf ) assert env_run.call_args_list == [ - mocker.call(f'ruff check --config {user_config_path} --fix .', shell=True), - mocker.call(f'ruff format --config {user_config_path} .', shell=True), + mocker.call(f"ruff check --config {user_config_path} --fix .", shell=True), + mocker.call(f"ruff format --config {user_config_path} .", shell=True), ] assert default_config.read_text() == defaults_file_stable - old_contents = (project_path / 'pyproject.toml').read_text() - config_path = str(default_config).replace('\\', '\\\\') + old_contents = (project_path / "pyproject.toml").read_text() + config_path = str(default_config).replace("\\", "\\\\") assert ( user_config.read_text() == f"""\ @@ -115,27 +115,27 @@ def test_fix(self, hatch, helpers, temp_dir, config_file, env_run, mocker, platf ) def test_check(self, hatch, helpers, temp_dir, config_file, env_run, mocker, platform, defaults_file_stable): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() - config_dir = data_path / 'env' / '.internal' / 'hatch-static-analysis' / '.config' / project_path.id - default_config = config_dir / 'ruff_defaults.toml' - user_config = config_dir / 'pyproject.toml' + config_dir = data_path / "env" / ".internal" / "hatch-static-analysis" / ".config" / project_path.id + default_config = config_dir / "ruff_defaults.toml" + user_config = config_dir / "pyproject.toml" user_config_path = platform.join_command_args([str(user_config)]) with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('fmt', '--check') + result = hatch("fmt", "--check") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -146,14 +146,14 @@ def test_check(self, hatch, helpers, temp_dir, config_file, env_run, mocker, pla ) assert env_run.call_args_list == [ - mocker.call(f'ruff check --config {user_config_path} .', shell=True), - mocker.call(f'ruff format --config {user_config_path} --check --diff .', shell=True), + mocker.call(f"ruff check --config {user_config_path} .", shell=True), + mocker.call(f"ruff format --config {user_config_path} --check --diff .", shell=True), ] assert default_config.read_text() == defaults_file_stable - old_contents = (project_path / 'pyproject.toml').read_text() - config_path = str(default_config).replace('\\', '\\\\') + old_contents = (project_path / "pyproject.toml").read_text() + config_path = str(default_config).replace("\\", "\\\\") assert ( user_config.read_text() == f"""\ @@ -165,31 +165,31 @@ def test_check(self, hatch, helpers, temp_dir, config_file, env_run, mocker, pla def test_existing_config( self, hatch, helpers, temp_dir, config_file, env_run, mocker, platform, defaults_file_stable ): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() - config_dir = data_path / 'env' / '.internal' / 'hatch-static-analysis' / '.config' / project_path.id - default_config = config_dir / 'ruff_defaults.toml' - user_config = config_dir / 'pyproject.toml' + config_dir = data_path / "env" / ".internal" / "hatch-static-analysis" / ".config" / project_path.id + default_config = config_dir / "ruff_defaults.toml" + user_config = config_dir / "pyproject.toml" user_config_path = platform.join_command_args([str(user_config)]) - project_file = project_path / 'pyproject.toml' + project_file = project_path / "pyproject.toml" old_contents = project_file.read_text() - project_file.write_text(f'[tool.ruff]\n{old_contents}') + project_file.write_text(f"[tool.ruff]\n{old_contents}") with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('fmt', '--check') + result = hatch("fmt", "--check") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -200,13 +200,13 @@ def test_existing_config( ) assert env_run.call_args_list == [ - mocker.call(f'ruff check --config {user_config_path} .', shell=True), - mocker.call(f'ruff format --config {user_config_path} --check --diff .', shell=True), + mocker.call(f"ruff check --config {user_config_path} .", shell=True), + mocker.call(f"ruff format --config {user_config_path} --check --diff .", shell=True), ] assert default_config.read_text() == defaults_file_stable - config_path = str(default_config).replace('\\', '\\\\') + config_path = str(default_config).replace("\\", "\\\\") assert ( user_config.read_text() == f"""\ @@ -218,27 +218,27 @@ def test_existing_config( class TestPreview: def test_fix_flag(self, hatch, helpers, temp_dir, config_file, env_run, mocker, platform, defaults_file_preview): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() - config_dir = data_path / 'env' / '.internal' / 'hatch-static-analysis' / '.config' / project_path.id - default_config = config_dir / 'ruff_defaults.toml' - user_config = config_dir / 'pyproject.toml' + config_dir = data_path / "env" / ".internal" / "hatch-static-analysis" / ".config" / project_path.id + default_config = config_dir / "ruff_defaults.toml" + user_config = config_dir / "pyproject.toml" user_config_path = platform.join_command_args([str(user_config)]) with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('fmt', '--preview') + result = hatch("fmt", "--preview") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -249,14 +249,14 @@ def test_fix_flag(self, hatch, helpers, temp_dir, config_file, env_run, mocker, ) assert env_run.call_args_list == [ - mocker.call(f'ruff check --config {user_config_path} --preview --fix .', shell=True), - mocker.call(f'ruff format --config {user_config_path} --preview .', shell=True), + mocker.call(f"ruff check --config {user_config_path} --preview --fix .", shell=True), + mocker.call(f"ruff format --config {user_config_path} --preview .", shell=True), ] assert default_config.read_text() == defaults_file_preview - old_contents = (project_path / 'pyproject.toml').read_text() - config_path = str(default_config).replace('\\', '\\\\') + old_contents = (project_path / "pyproject.toml").read_text() + config_path = str(default_config).replace("\\", "\\\\") assert ( user_config.read_text() == f"""\ @@ -266,27 +266,27 @@ def test_fix_flag(self, hatch, helpers, temp_dir, config_file, env_run, mocker, ) def test_check_flag(self, hatch, helpers, temp_dir, config_file, env_run, mocker, platform, defaults_file_preview): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() - config_dir = data_path / 'env' / '.internal' / 'hatch-static-analysis' / '.config' / project_path.id - default_config = config_dir / 'ruff_defaults.toml' - user_config = config_dir / 'pyproject.toml' + config_dir = data_path / "env" / ".internal" / "hatch-static-analysis" / ".config" / project_path.id + default_config = config_dir / "ruff_defaults.toml" + user_config = config_dir / "pyproject.toml" user_config_path = platform.join_command_args([str(user_config)]) with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('fmt', '--check', '--preview') + result = hatch("fmt", "--check", "--preview") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -297,14 +297,14 @@ def test_check_flag(self, hatch, helpers, temp_dir, config_file, env_run, mocker ) assert env_run.call_args_list == [ - mocker.call(f'ruff check --config {user_config_path} --preview .', shell=True), - mocker.call(f'ruff format --config {user_config_path} --preview --check --diff .', shell=True), + mocker.call(f"ruff check --config {user_config_path} --preview .", shell=True), + mocker.call(f"ruff format --config {user_config_path} --preview --check --diff .", shell=True), ] assert default_config.read_text() == defaults_file_preview - old_contents = (project_path / 'pyproject.toml').read_text() - config_path = str(default_config).replace('\\', '\\\\') + old_contents = (project_path / "pyproject.toml").read_text() + config_path = str(default_config).replace("\\", "\\\\") assert ( user_config.read_text() == f"""\ @@ -316,40 +316,40 @@ def test_check_flag(self, hatch, helpers, temp_dir, config_file, env_run, mocker class TestComponents: def test_only_linter(self, hatch, temp_dir, config_file, env_run, mocker, platform, defaults_file_stable): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('fmt', '--linter') + result = hatch("fmt", "--linter") assert result.exit_code == 0, result.output assert not result.output - root_data_path = data_path / 'env' / '.internal' / 'hatch-static-analysis' / '.config' + root_data_path = data_path / "env" / ".internal" / "hatch-static-analysis" / ".config" config_dir = next(root_data_path.iterdir()) - default_config = config_dir / 'ruff_defaults.toml' - user_config = config_dir / 'pyproject.toml' + default_config = config_dir / "ruff_defaults.toml" + user_config = config_dir / "pyproject.toml" user_config_path = platform.join_command_args([str(user_config)]) assert env_run.call_args_list == [ - mocker.call(f'ruff check --config {user_config_path} --fix .', shell=True), + mocker.call(f"ruff check --config {user_config_path} --fix .", shell=True), ] assert default_config.read_text() == defaults_file_stable - old_contents = (project_path / 'pyproject.toml').read_text() - config_path = str(default_config).replace('\\', '\\\\') + old_contents = (project_path / "pyproject.toml").read_text() + config_path = str(default_config).replace("\\", "\\\\") assert ( user_config.read_text() == f"""\ @@ -359,40 +359,40 @@ def test_only_linter(self, hatch, temp_dir, config_file, env_run, mocker, platfo ) def test_only_formatter(self, hatch, temp_dir, config_file, env_run, mocker, platform, defaults_file_stable): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('fmt', '--formatter') + result = hatch("fmt", "--formatter") assert result.exit_code == 0, result.output assert not result.output - root_data_path = data_path / 'env' / '.internal' / 'hatch-static-analysis' / '.config' + root_data_path = data_path / "env" / ".internal" / "hatch-static-analysis" / ".config" config_dir = next(root_data_path.iterdir()) - default_config = config_dir / 'ruff_defaults.toml' - user_config = config_dir / 'pyproject.toml' + default_config = config_dir / "ruff_defaults.toml" + user_config = config_dir / "pyproject.toml" user_config_path = platform.join_command_args([str(user_config)]) assert env_run.call_args_list == [ - mocker.call(f'ruff format --config {user_config_path} .', shell=True), + mocker.call(f"ruff format --config {user_config_path} .", shell=True), ] assert default_config.read_text() == defaults_file_stable - old_contents = (project_path / 'pyproject.toml').read_text() - config_path = str(default_config).replace('\\', '\\\\') + old_contents = (project_path / "pyproject.toml").read_text() + config_path = str(default_config).replace("\\", "\\\\") assert ( user_config.read_text() == f"""\ @@ -401,24 +401,24 @@ def test_only_formatter(self, hatch, temp_dir, config_file, env_run, mocker, pla extend = "{config_path}\"""" ) - @pytest.mark.usefixtures('env_run') + @pytest.mark.usefixtures("env_run") def test_select_multiple(self, hatch, helpers, temp_dir, config_file): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('fmt', '--linter', '--formatter') + result = hatch("fmt", "--linter", "--formatter") assert result.exit_code == 1, result.output assert result.output == helpers.dedent( @@ -430,27 +430,27 @@ def test_select_multiple(self, hatch, helpers, temp_dir, config_file): class TestArguments: def test_forwarding(self, hatch, helpers, temp_dir, config_file, env_run, mocker, platform, defaults_file_stable): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() - config_dir = data_path / 'env' / '.internal' / 'hatch-static-analysis' / '.config' / project_path.id - default_config = config_dir / 'ruff_defaults.toml' - user_config = config_dir / 'pyproject.toml' + config_dir = data_path / "env" / ".internal" / "hatch-static-analysis" / ".config" / project_path.id + default_config = config_dir / "ruff_defaults.toml" + user_config = config_dir / "pyproject.toml" user_config_path = platform.join_command_args([str(user_config)]) with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('fmt', '--', '--foo', 'bar') + result = hatch("fmt", "--", "--foo", "bar") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -461,14 +461,14 @@ def test_forwarding(self, hatch, helpers, temp_dir, config_file, env_run, mocker ) assert env_run.call_args_list == [ - mocker.call(f'ruff check --config {user_config_path} --fix --foo bar', shell=True), - mocker.call(f'ruff format --config {user_config_path} --foo bar', shell=True), + mocker.call(f"ruff check --config {user_config_path} --fix --foo bar", shell=True), + mocker.call(f"ruff format --config {user_config_path} --foo bar", shell=True), ] assert default_config.read_text() == defaults_file_stable - old_contents = (project_path / 'pyproject.toml').read_text() - config_path = str(default_config).replace('\\', '\\\\') + old_contents = (project_path / "pyproject.toml").read_text() + config_path = str(default_config).replace("\\", "\\\\") assert ( user_config.read_text() == f"""\ @@ -479,24 +479,24 @@ def test_forwarding(self, hatch, helpers, temp_dir, config_file, env_run, mocker class TestConfigPath: - @pytest.mark.usefixtures('env_run') + @pytest.mark.usefixtures("env_run") def test_sync_without_config(self, hatch, helpers, temp_dir, config_file): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('fmt', '--sync') + result = hatch("fmt", "--sync") assert result.exit_code == 1, result.output assert result.output == helpers.dedent( @@ -506,30 +506,30 @@ def test_sync_without_config(self, hatch, helpers, temp_dir, config_file): ) def test_sync(self, hatch, helpers, temp_dir, config_file, env_run, mocker, defaults_file_stable): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() - default_config_file = project_path / 'ruff_defaults.toml' + default_config_file = project_path / "ruff_defaults.toml" assert not default_config_file.is_file() project = Project(project_path) config = dict(project.raw_config) - config['tool']['hatch']['envs'] = {'hatch-static-analysis': {'config-path': 'ruff_defaults.toml'}} - config['tool']['ruff'] = {'extend': 'ruff_defaults.toml'} + config["tool"]["hatch"]["envs"] = {"hatch-static-analysis": {"config-path": "ruff_defaults.toml"}} + config["tool"]["ruff"] = {"extend": "ruff_defaults.toml"} project.save_config(config) with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('fmt', '--sync') + result = hatch("fmt", "--sync") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -539,41 +539,41 @@ def test_sync(self, hatch, helpers, temp_dir, config_file, env_run, mocker, defa """ ) - root_data_path = data_path / 'env' / '.internal' / 'hatch-static-analysis' / '.config' + root_data_path = data_path / "env" / ".internal" / "hatch-static-analysis" / ".config" assert not root_data_path.is_dir() assert env_run.call_args_list == [ - mocker.call('ruff check --fix .', shell=True), - mocker.call('ruff format .', shell=True), + mocker.call("ruff check --fix .", shell=True), + mocker.call("ruff format .", shell=True), ] assert default_config_file.read_text() == defaults_file_stable def test_no_sync(self, hatch, helpers, temp_dir, config_file, env_run, mocker): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() - default_config_file = project_path / 'ruff_defaults.toml' + default_config_file = project_path / "ruff_defaults.toml" default_config_file.touch() project = Project(project_path) config = dict(project.raw_config) - config['tool']['hatch']['envs'] = {'hatch-static-analysis': {'config-path': 'ruff_defaults.toml'}} - config['tool']['ruff'] = {'extend': 'ruff_defaults.toml'} + config["tool"]["hatch"]["envs"] = {"hatch-static-analysis": {"config-path": "ruff_defaults.toml"}} + config["tool"]["ruff"] = {"extend": "ruff_defaults.toml"} project.save_config(config) with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('fmt') + result = hatch("fmt") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -583,41 +583,41 @@ def test_no_sync(self, hatch, helpers, temp_dir, config_file, env_run, mocker): """ ) - root_data_path = data_path / 'env' / '.internal' / 'hatch-static-analysis' / '.config' + root_data_path = data_path / "env" / ".internal" / "hatch-static-analysis" / ".config" assert not root_data_path.is_dir() assert env_run.call_args_list == [ - mocker.call('ruff check --fix .', shell=True), - mocker.call('ruff format .', shell=True), + mocker.call("ruff check --fix .", shell=True), + mocker.call("ruff format .", shell=True), ] assert not default_config_file.read_text() def test_sync_legacy_config(self, hatch, helpers, temp_dir, config_file, env_run, mocker, defaults_file_stable): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() - default_config_file = project_path / 'ruff_defaults.toml' + default_config_file = project_path / "ruff_defaults.toml" assert not default_config_file.is_file() project = Project(project_path) config = dict(project.raw_config) - config['tool']['hatch']['format'] = {'config-path': 'ruff_defaults.toml'} - config['tool']['ruff'] = {'extend': 'ruff_defaults.toml'} + config["tool"]["hatch"]["format"] = {"config-path": "ruff_defaults.toml"} + config["tool"]["ruff"] = {"extend": "ruff_defaults.toml"} project.save_config(config) with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('fmt', '--sync') + result = hatch("fmt", "--sync") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -628,12 +628,12 @@ def test_sync_legacy_config(self, hatch, helpers, temp_dir, config_file, env_run """ ) - root_data_path = data_path / 'env' / '.internal' / 'hatch-static-analysis' / '.config' + root_data_path = data_path / "env" / ".internal" / "hatch-static-analysis" / ".config" assert not root_data_path.is_dir() assert env_run.call_args_list == [ - mocker.call('ruff check --fix .', shell=True), - mocker.call('ruff format .', shell=True), + mocker.call("ruff check --fix .", shell=True), + mocker.call("ruff format .", shell=True), ] assert default_config_file.read_text() == defaults_file_stable @@ -641,144 +641,144 @@ def test_sync_legacy_config(self, hatch, helpers, temp_dir, config_file, env_run class TestCustomScripts: def test_only_linter_fix(self, hatch, temp_dir, config_file, env_run, mocker): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() project = Project(project_path) config = dict(project.raw_config) - config['tool']['hatch']['envs'] = { - 'hatch-static-analysis': { - 'config-path': 'none', - 'dependencies': ['black', 'flake8', 'isort'], - 'scripts': { - 'format-check': [ - 'black --check --diff {args:.}', - 'isort --check-only --diff {args:.}', + config["tool"]["hatch"]["envs"] = { + "hatch-static-analysis": { + "config-path": "none", + "dependencies": ["black", "flake8", "isort"], + "scripts": { + "format-check": [ + "black --check --diff {args:.}", + "isort --check-only --diff {args:.}", ], - 'format-fix': [ - 'isort {args:.}', - 'black {args:.}', + "format-fix": [ + "isort {args:.}", + "black {args:.}", ], - 'lint-check': 'flake8 {args:.}', - 'lint-fix': 'lint-check', + "lint-check": "flake8 {args:.}", + "lint-fix": "lint-check", }, } } project.save_config(config) with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('fmt', '--linter') + result = hatch("fmt", "--linter") assert result.exit_code == 0, result.output assert not result.output - root_data_path = data_path / 'env' / '.internal' / 'hatch-static-analysis' / '.config' + root_data_path = data_path / "env" / ".internal" / "hatch-static-analysis" / ".config" assert not root_data_path.is_dir() assert env_run.call_args_list == [ - mocker.call('flake8 .', shell=True), + mocker.call("flake8 .", shell=True), ] def test_only_linter_check(self, hatch, temp_dir, config_file, env_run, mocker): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() project = Project(project_path) config = dict(project.raw_config) - config['tool']['hatch']['envs'] = { - 'hatch-static-analysis': { - 'config-path': 'none', - 'dependencies': ['black', 'flake8', 'isort'], - 'scripts': { - 'format-check': [ - 'black --check --diff {args:.}', - 'isort --check-only --diff {args:.}', + config["tool"]["hatch"]["envs"] = { + "hatch-static-analysis": { + "config-path": "none", + "dependencies": ["black", "flake8", "isort"], + "scripts": { + "format-check": [ + "black --check --diff {args:.}", + "isort --check-only --diff {args:.}", ], - 'format-fix': [ - 'isort {args:.}', - 'black {args:.}', + "format-fix": [ + "isort {args:.}", + "black {args:.}", ], - 'lint-check': 'flake8 {args:.}', - 'lint-fix': 'lint-check', + "lint-check": "flake8 {args:.}", + "lint-fix": "lint-check", }, } } project.save_config(config) with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('fmt', '--check', '--linter') + result = hatch("fmt", "--check", "--linter") assert result.exit_code == 0, result.output assert not result.output - root_data_path = data_path / 'env' / '.internal' / 'hatch-static-analysis' / '.config' + root_data_path = data_path / "env" / ".internal" / "hatch-static-analysis" / ".config" assert not root_data_path.is_dir() assert env_run.call_args_list == [ - mocker.call('flake8 .', shell=True), + mocker.call("flake8 .", shell=True), ] def test_only_formatter_fix(self, hatch, helpers, temp_dir, config_file, env_run, mocker): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() project = Project(project_path) config = dict(project.raw_config) - config['tool']['hatch']['envs'] = { - 'hatch-static-analysis': { - 'config-path': 'none', - 'dependencies': ['black', 'flake8', 'isort'], - 'scripts': { - 'format-check': [ - 'black --check --diff {args:.}', - 'isort --check-only --diff {args:.}', + config["tool"]["hatch"]["envs"] = { + "hatch-static-analysis": { + "config-path": "none", + "dependencies": ["black", "flake8", "isort"], + "scripts": { + "format-check": [ + "black --check --diff {args:.}", + "isort --check-only --diff {args:.}", ], - 'format-fix': [ - 'isort {args:.}', - 'black {args:.}', + "format-fix": [ + "isort {args:.}", + "black {args:.}", ], - 'lint-check': 'flake8 {args:.}', - 'lint-fix': 'lint-check', + "lint-check": "flake8 {args:.}", + "lint-fix": "lint-check", }, } } project.save_config(config) with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('fmt', '--formatter') + result = hatch("fmt", "--formatter") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -788,53 +788,53 @@ def test_only_formatter_fix(self, hatch, helpers, temp_dir, config_file, env_run """ ) - root_data_path = data_path / 'env' / '.internal' / 'hatch-static-analysis' / '.config' + root_data_path = data_path / "env" / ".internal" / "hatch-static-analysis" / ".config" assert not root_data_path.is_dir() assert env_run.call_args_list == [ - mocker.call('isort .', shell=True), - mocker.call('black .', shell=True), + mocker.call("isort .", shell=True), + mocker.call("black .", shell=True), ] def test_only_formatter_check(self, hatch, helpers, temp_dir, config_file, env_run, mocker): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() project = Project(project_path) config = dict(project.raw_config) - config['tool']['hatch']['envs'] = { - 'hatch-static-analysis': { - 'config-path': 'none', - 'dependencies': ['black', 'flake8', 'isort'], - 'scripts': { - 'format-check': [ - 'black --check --diff {args:.}', - 'isort --check-only --diff {args:.}', + config["tool"]["hatch"]["envs"] = { + "hatch-static-analysis": { + "config-path": "none", + "dependencies": ["black", "flake8", "isort"], + "scripts": { + "format-check": [ + "black --check --diff {args:.}", + "isort --check-only --diff {args:.}", ], - 'format-fix': [ - 'isort {args:.}', - 'black {args:.}', + "format-fix": [ + "isort {args:.}", + "black {args:.}", ], - 'lint-check': 'flake8 {args:.}', - 'lint-fix': 'lint-check', + "lint-check": "flake8 {args:.}", + "lint-fix": "lint-check", }, } } project.save_config(config) with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('fmt', '--check', '--formatter') + result = hatch("fmt", "--check", "--formatter") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -844,53 +844,53 @@ def test_only_formatter_check(self, hatch, helpers, temp_dir, config_file, env_r """ ) - root_data_path = data_path / 'env' / '.internal' / 'hatch-static-analysis' / '.config' + root_data_path = data_path / "env" / ".internal" / "hatch-static-analysis" / ".config" assert not root_data_path.is_dir() assert env_run.call_args_list == [ - mocker.call('black --check --diff .', shell=True), - mocker.call('isort --check-only --diff .', shell=True), + mocker.call("black --check --diff .", shell=True), + mocker.call("isort --check-only --diff .", shell=True), ] def test_fix(self, hatch, helpers, temp_dir, config_file, env_run, mocker): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() project = Project(project_path) config = dict(project.raw_config) - config['tool']['hatch']['envs'] = { - 'hatch-static-analysis': { - 'config-path': 'none', - 'dependencies': ['black', 'flake8', 'isort'], - 'scripts': { - 'format-check': [ - 'black --check --diff {args:.}', - 'isort --check-only --diff {args:.}', + config["tool"]["hatch"]["envs"] = { + "hatch-static-analysis": { + "config-path": "none", + "dependencies": ["black", "flake8", "isort"], + "scripts": { + "format-check": [ + "black --check --diff {args:.}", + "isort --check-only --diff {args:.}", ], - 'format-fix': [ - 'isort {args:.}', - 'black {args:.}', + "format-fix": [ + "isort {args:.}", + "black {args:.}", ], - 'lint-check': 'flake8 {args:.}', - 'lint-fix': 'lint-check', + "lint-check": "flake8 {args:.}", + "lint-fix": "lint-check", }, } } project.save_config(config) with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('fmt') + result = hatch("fmt") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -901,54 +901,54 @@ def test_fix(self, hatch, helpers, temp_dir, config_file, env_run, mocker): """ ) - root_data_path = data_path / 'env' / '.internal' / 'hatch-static-analysis' / '.config' + root_data_path = data_path / "env" / ".internal" / "hatch-static-analysis" / ".config" assert not root_data_path.is_dir() assert env_run.call_args_list == [ - mocker.call('flake8 .', shell=True), - mocker.call('isort .', shell=True), - mocker.call('black .', shell=True), + mocker.call("flake8 .", shell=True), + mocker.call("isort .", shell=True), + mocker.call("black .", shell=True), ] def test_check(self, hatch, helpers, temp_dir, config_file, env_run, mocker): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() project = Project(project_path) config = dict(project.raw_config) - config['tool']['hatch']['envs'] = { - 'hatch-static-analysis': { - 'config-path': 'none', - 'dependencies': ['black', 'flake8', 'isort'], - 'scripts': { - 'format-check': [ - 'black --check --diff {args:.}', - 'isort --check-only --diff {args:.}', + config["tool"]["hatch"]["envs"] = { + "hatch-static-analysis": { + "config-path": "none", + "dependencies": ["black", "flake8", "isort"], + "scripts": { + "format-check": [ + "black --check --diff {args:.}", + "isort --check-only --diff {args:.}", ], - 'format-fix': [ - 'isort {args:.}', - 'black {args:.}', + "format-fix": [ + "isort {args:.}", + "black {args:.}", ], - 'lint-check': 'flake8 {args:.}', - 'lint-fix': 'lint-check', + "lint-check": "flake8 {args:.}", + "lint-fix": "lint-check", }, } } project.save_config(config) with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('fmt', '--check') + result = hatch("fmt", "--check") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -959,11 +959,11 @@ def test_check(self, hatch, helpers, temp_dir, config_file, env_run, mocker): """ ) - root_data_path = data_path / 'env' / '.internal' / 'hatch-static-analysis' / '.config' + root_data_path = data_path / "env" / ".internal" / "hatch-static-analysis" / ".config" assert not root_data_path.is_dir() assert env_run.call_args_list == [ - mocker.call('flake8 .', shell=True), - mocker.call('black --check --diff .', shell=True), - mocker.call('isort --check-only --diff .', shell=True), + mocker.call("flake8 .", shell=True), + mocker.call("black --check --diff .", shell=True), + mocker.call("isort --check-only --diff .", shell=True), ] diff --git a/tests/cli/new/test_new.py b/tests/cli/new/test_new.py index 0791fcfe9..95d19bf49 100644 --- a/tests/cli/new/test_new.py +++ b/tests/cli/new/test_new.py @@ -4,62 +4,62 @@ def remove_trailing_spaces(text): - return ''.join(f'{line.rstrip()}\n' for line in text.splitlines(True)) + return "".join(f"{line.rstrip()}\n" for line in text.splitlines(True)) class TestErrors: def test_path_is_file(self, hatch, temp_dir): with temp_dir.as_cwd(): - path = temp_dir / 'foo' + path = temp_dir / "foo" path.touch() - result = hatch('new', 'foo') + result = hatch("new", "foo") assert result.exit_code == 1 - assert result.output == f'Path `{path}` points to a file.\n' + assert result.output == f"Path `{path}` points to a file.\n" def test_path_not_empty(self, hatch, temp_dir): with temp_dir.as_cwd(): - path = temp_dir / 'foo' - (path / 'bar').ensure_dir_exists() + path = temp_dir / "foo" + (path / "bar").ensure_dir_exists() - result = hatch('new', 'foo') + result = hatch("new", "foo") assert result.exit_code == 1 - assert result.output == f'Directory `{path}` is not empty.\n' + assert result.output == f"Directory `{path}` is not empty.\n" def test_no_plugins_found(self, hatch, config_file, temp_dir): - project_name = 'My.App' - config_file.model.template.plugins = {'foo': {}} + project_name = "My.App" + config_file.model.template.plugins = {"foo": {}} config_file.save() with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 1 - assert result.output == 'None of the defined plugins were found: foo\n' + assert result.output == "None of the defined plugins were found: foo\n" def test_some_not_plugins_found(self, hatch, config_file, temp_dir): - project_name = 'My.App' - config_file.model.template.plugins['foo'] = {} + project_name = "My.App" + config_file.model.template.plugins["foo"] = {} config_file.save() with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 1 - assert result.output == 'Some of the defined plugins were not found: foo\n' + assert result.output == "Some of the defined plugins were not found: foo\n" def test_default(hatch, helpers, temp_dir): - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) - path = temp_dir / 'my-app' + path = temp_dir / "my-app" - expected_files = helpers.get_template_files('new.default', project_name) + expected_files = helpers.get_template_files("new.default", project_name) helpers.assert_files(path, expected_files) assert result.exit_code == 0, result.output @@ -80,12 +80,12 @@ def test_default(hatch, helpers, temp_dir): def test_default_explicit_path(hatch, helpers, temp_dir): - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name, '.') + result = hatch("new", project_name, ".") - expected_files = helpers.get_template_files('new.default', project_name) + expected_files = helpers.get_template_files("new.default", project_name) helpers.assert_files(temp_dir, expected_files) assert result.exit_code == 0, result.output @@ -105,16 +105,16 @@ def test_default_explicit_path(hatch, helpers, temp_dir): def test_default_empty_plugins_table(hatch, helpers, config_file, temp_dir): - project_name = 'My.App' + project_name = "My.App" config_file.model.template.plugins = {} config_file.save() with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) - path = temp_dir / 'my-app' + path = temp_dir / "my-app" - expected_files = helpers.get_template_files('new.default', project_name) + expected_files = helpers.get_template_files("new.default", project_name) helpers.assert_files(path, expected_files) assert result.exit_code == 0, result.output @@ -136,16 +136,16 @@ def test_default_empty_plugins_table(hatch, helpers, config_file, temp_dir): @pytest.mark.requires_internet def test_default_no_license_cache(hatch, helpers, temp_dir): - project_name = 'My.App' - cache_dir = temp_dir / 'cache' + project_name = "My.App" + cache_dir = temp_dir / "cache" cache_dir.mkdir() with temp_dir.as_cwd({ConfigEnvVars.CACHE: str(cache_dir)}): - result = hatch('new', project_name) + result = hatch("new", project_name) - path = temp_dir / 'my-app' + path = temp_dir / "my-app" - expected_files = helpers.get_template_files('new.default', project_name) + expected_files = helpers.get_template_files("new.default", project_name) helpers.assert_files(path, expected_files) assert result.exit_code == 0, result.output @@ -166,16 +166,16 @@ def test_default_no_license_cache(hatch, helpers, temp_dir): def test_licenses_multiple(hatch, helpers, config_file, temp_dir): - project_name = 'My.App' - config_file.model.template.licenses.default = ['MIT', 'Apache-2.0'] + project_name = "My.App" + config_file.model.template.licenses.default = ["MIT", "Apache-2.0"] config_file.save() with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) - path = temp_dir / 'my-app' + path = temp_dir / "my-app" - expected_files = helpers.get_template_files('new.licenses_multiple', project_name) + expected_files = helpers.get_template_files("new.licenses_multiple", project_name) helpers.assert_files(path, expected_files) assert result.exit_code == 0, result.output @@ -198,16 +198,16 @@ def test_licenses_multiple(hatch, helpers, config_file, temp_dir): def test_licenses_empty(hatch, helpers, config_file, temp_dir): - project_name = 'My.App' + project_name = "My.App" config_file.model.template.licenses.default = [] config_file.save() with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) - path = temp_dir / 'my-app' + path = temp_dir / "my-app" - expected_files = helpers.get_template_files('new.licenses_empty', project_name) + expected_files = helpers.get_template_files("new.licenses_empty", project_name) helpers.assert_files(path, expected_files) assert result.exit_code == 0, result.output @@ -227,20 +227,20 @@ def test_licenses_empty(hatch, helpers, config_file, temp_dir): def test_projects_urls_space_in_label(hatch, helpers, config_file, temp_dir): - project_name = 'My.App' - config_file.model.template.plugins['default']['project_urls'] = { - 'Documentation': 'https://github.com/{name}/{project_name_normalized}#readme', - 'Source': 'https://github.com/{name}/{project_name_normalized}', - 'Bug Tracker': 'https://github.com/{name}/{project_name_normalized}/issues', + project_name = "My.App" + config_file.model.template.plugins["default"]["project_urls"] = { + "Documentation": "https://github.com/{name}/{project_name_normalized}#readme", + "Source": "https://github.com/{name}/{project_name_normalized}", + "Bug Tracker": "https://github.com/{name}/{project_name_normalized}/issues", } config_file.save() with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) - path = temp_dir / 'my-app' + path = temp_dir / "my-app" - expected_files = helpers.get_template_files('new.projects_urls_space_in_label', project_name) + expected_files = helpers.get_template_files("new.projects_urls_space_in_label", project_name) helpers.assert_files(path, expected_files) assert result.exit_code == 0, result.output @@ -261,16 +261,16 @@ def test_projects_urls_space_in_label(hatch, helpers, config_file, temp_dir): def test_projects_urls_empty(hatch, helpers, config_file, temp_dir): - project_name = 'My.App' - config_file.model.template.plugins['default']['project_urls'] = {} + project_name = "My.App" + config_file.model.template.plugins["default"]["project_urls"] = {} config_file.save() with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) - path = temp_dir / 'my-app' + path = temp_dir / "my-app" - expected_files = helpers.get_template_files('new.projects_urls_empty', project_name) + expected_files = helpers.get_template_files("new.projects_urls_empty", project_name) helpers.assert_files(path, expected_files) assert result.exit_code == 0, result.output @@ -291,14 +291,14 @@ def test_projects_urls_empty(hatch, helpers, config_file, temp_dir): def test_feature_cli(hatch, helpers, temp_dir): - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name, '--cli') + result = hatch("new", project_name, "--cli") - path = temp_dir / 'my-app' + path = temp_dir / "my-app" - expected_files = helpers.get_template_files('new.feature_cli', project_name) + expected_files = helpers.get_template_files("new.feature_cli", project_name) helpers.assert_files(path, expected_files) assert result.exit_code == 0, result.output @@ -322,17 +322,17 @@ def test_feature_cli(hatch, helpers, temp_dir): def test_feature_ci(hatch, helpers, config_file, temp_dir): - config_file.model.template.plugins['default']['ci'] = True + config_file.model.template.plugins["default"]["ci"] = True config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) - path = temp_dir / 'my-app' + path = temp_dir / "my-app" - expected_files = helpers.get_template_files('new.feature_ci', project_name) + expected_files = helpers.get_template_files("new.feature_ci", project_name) helpers.assert_files(path, expected_files) assert result.exit_code == 0, result.output @@ -356,17 +356,17 @@ def test_feature_ci(hatch, helpers, config_file, temp_dir): def test_feature_no_src_layout(hatch, helpers, config_file, temp_dir): - config_file.model.template.plugins['default']['src-layout'] = False + config_file.model.template.plugins["default"]["src-layout"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) - path = temp_dir / 'my-app' + path = temp_dir / "my-app" - expected_files = helpers.get_template_files('new.feature_no_src_layout', project_name) + expected_files = helpers.get_template_files("new.feature_no_src_layout", project_name) helpers.assert_files(path, expected_files) assert result.exit_code == 0, result.output @@ -386,17 +386,17 @@ def test_feature_no_src_layout(hatch, helpers, config_file, temp_dir): def test_feature_tests_disable(hatch, helpers, config_file, temp_dir): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) - path = temp_dir / 'my-app' + path = temp_dir / "my-app" - expected_files = helpers.get_template_files('new.basic', project_name) + expected_files = helpers.get_template_files("new.basic", project_name) helpers.assert_files(path, expected_files) assert result.exit_code == 0, result.output @@ -416,7 +416,7 @@ def test_feature_tests_disable(hatch, helpers, config_file, temp_dir): def test_no_project_name_error(hatch, helpers, temp_dir): with temp_dir.as_cwd(): - result = hatch('new') + result = hatch("new") assert result.exit_code == 1 assert remove_trailing_spaces(result.output) == helpers.dedent( @@ -427,15 +427,15 @@ def test_no_project_name_error(hatch, helpers, temp_dir): def test_interactive(hatch, helpers, temp_dir): - project_name = 'My.App' - description = 'foo \u2764' + project_name = "My.App" + description = "foo \u2764" with temp_dir.as_cwd(): - result = hatch('new', '-i', input=f'{project_name}\n{description}') + result = hatch("new", "-i", input=f"{project_name}\n{description}") - path = temp_dir / 'my-app' + path = temp_dir / "my-app" - expected_files = helpers.get_template_files('new.default', project_name, description=description) + expected_files = helpers.get_template_files("new.default", project_name, description=description) helpers.assert_files(path, expected_files) assert result.exit_code == 0, result.output @@ -459,15 +459,15 @@ def test_interactive(hatch, helpers, temp_dir): def test_no_project_name_enables_interactive(hatch, helpers, temp_dir): - project_name = 'My.App' - description = 'foo' + project_name = "My.App" + description = "foo" with temp_dir.as_cwd(): - result = hatch('new', '-i', input=f'{project_name}\n{description}') + result = hatch("new", "-i", input=f"{project_name}\n{description}") - path = temp_dir / 'my-app' + path = temp_dir / "my-app" - expected_files = helpers.get_template_files('new.default', project_name, description=description) + expected_files = helpers.get_template_files("new.default", project_name, description=description) helpers.assert_files(path, expected_files) assert result.exit_code == 0, result.output @@ -491,23 +491,23 @@ def test_no_project_name_enables_interactive(hatch, helpers, temp_dir): def test_initialize_fresh(hatch, helpers, temp_dir): - project_name = 'My.App' - description = 'foo' + project_name = "My.App" + description = "foo" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - path = temp_dir / 'my-app' + path = temp_dir / "my-app" - project_file = path / 'pyproject.toml' + project_file = path / "pyproject.toml" project_file.remove() assert not project_file.is_file() with path.as_cwd(): - result = hatch('new', '--init', input=f'{project_name}\n{description}') + result = hatch("new", "--init", input=f"{project_name}\n{description}") - expected_files = helpers.get_template_files('new.default', project_name, description=description) + expected_files = helpers.get_template_files("new.default", project_name, description=description) helpers.assert_files(path, expected_files) assert result.exit_code == 0, result.output @@ -522,10 +522,10 @@ def test_initialize_fresh(hatch, helpers, temp_dir): def test_initialize_update(hatch, helpers, temp_dir): - project_name = 'My.App' - description = 'foo' + project_name = "My.App" + description = "foo" - project_file = temp_dir / 'pyproject.toml' + project_file = temp_dir / "pyproject.toml" project_file.write_text( """\ [build-system] @@ -546,7 +546,7 @@ def test_initialize_update(hatch, helpers, temp_dir): ) with temp_dir.as_cwd(): - result = hatch('new', '--init', input=f'{project_name}\n{description}') + result = hatch("new", "--init", input=f"{project_name}\n{description}") assert result.exit_code == 0, result.output assert remove_trailing_spaces(result.output) == helpers.dedent( @@ -582,7 +582,7 @@ def test_initialize_setup_cfg_only(hatch, helpers, temp_dir): """ Test initializing a project with a setup.cfg file only. """ - setup_cfg_file = temp_dir / 'setup.cfg' + setup_cfg_file = temp_dir / "setup.cfg" setup_cfg_file.write_text( """\ [metadata] @@ -597,7 +597,7 @@ def test_initialize_setup_cfg_only(hatch, helpers, temp_dir): ) with temp_dir.as_cwd(): - result = hatch('new', '--init') + result = hatch("new", "--init") assert result.exit_code == 0, result.output assert remove_trailing_spaces(result.output) == helpers.dedent( @@ -606,7 +606,7 @@ def test_initialize_setup_cfg_only(hatch, helpers, temp_dir): """ ) - project_file = temp_dir / 'pyproject.toml' + project_file = temp_dir / "pyproject.toml" assert project_file.read_text() == ( """\ [build-system] diff --git a/tests/cli/project/test_metadata.py b/tests/cli/project/test_metadata.py index c722ebfe4..ae918f37d 100644 --- a/tests/cli/project/test_metadata.py +++ b/tests/cli/project/test_metadata.py @@ -6,38 +6,38 @@ from hatch.project.core import Project from hatchling.utils.constants import DEFAULT_CONFIG_FILE -pytestmark = [pytest.mark.usefixtures('mock_backend_process_output')] +pytestmark = [pytest.mark.usefixtures("mock_backend_process_output")] def read_readme(project_dir): - return repr((project_dir / 'README.txt').read_text())[1:-1] + return repr((project_dir / "README.txt").read_text())[1:-1] def test_other_backend(hatch, temp_dir, helpers): - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() - (path / 'README.md').replace(path / 'README.txt') + (path / "README.md").replace(path / "README.txt") project = Project(path) config = dict(project.raw_config) - config['build-system']['requires'] = ['flit-core==3.10.1'] - config['build-system']['build-backend'] = 'flit_core.buildapi' - config['project']['version'] = '0.0.1' - config['project']['dynamic'] = [] - config['project']['readme'] = 'README.txt' - del config['project']['license'] + config["build-system"]["requires"] = ["flit-core==3.10.1"] + config["build-system"]["build-backend"] = "flit_core.buildapi" + config["project"]["version"] = "0.0.1" + config["project"]["dynamic"] = [] + config["project"]["readme"] = "README.txt" + del config["project"]["license"] project.save_config(config) with path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('project', 'metadata') + result = hatch("project", "metadata") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -85,25 +85,25 @@ def test_other_backend(hatch, temp_dir, helpers): def test_default_all(hatch, temp_dir, helpers): - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() - (path / 'README.md').replace(path / 'README.txt') + (path / "README.md").replace(path / "README.txt") project = Project(path) config = dict(project.raw_config) - config['project']['readme'] = 'README.txt' + config["project"]["readme"] = "README.txt" project.save_config(config) with path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('project', 'metadata') + result = hatch("project", "metadata") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -149,25 +149,25 @@ def test_default_all(hatch, temp_dir, helpers): def test_field_readme(hatch, temp_dir): - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() - (path / 'README.md').replace(path / 'README.txt') + (path / "README.md").replace(path / "README.txt") project = Project(path) config = dict(project.raw_config) - config['project']['readme'] = 'README.txt' + config["project"]["readme"] = "README.txt" project.save_config(config) with path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('project', 'metadata', 'readme') + result = hatch("project", "metadata", "readme") assert result.exit_code == 0, result.output assert result.output == ( @@ -176,24 +176,24 @@ def test_field_readme(hatch, temp_dir): Checking dependencies Syncing dependencies Inspecting build dependencies -{(path / 'README.txt').read_text()} +{(path / "README.txt").read_text()} """ ) def test_field_string(hatch, temp_dir, helpers): - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() with path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('project', 'metadata', 'license') + result = hatch("project", "metadata", "license") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -208,18 +208,18 @@ def test_field_string(hatch, temp_dir, helpers): def test_field_complex(hatch, temp_dir, helpers): - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() with path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('project', 'metadata', 'urls') + result = hatch("project", "metadata", "urls") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -239,21 +239,21 @@ def test_field_complex(hatch, temp_dir, helpers): @pytest.mark.allow_backend_process def test_incompatible_environment(hatch, temp_dir, helpers, build_env_config): - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() project = Project(path) - helpers.update_project_environment(project, 'hatch-build', {'python': '9000', **build_env_config}) + helpers.update_project_environment(project, "hatch-build", {"python": "9000", **build_env_config}) with path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('project', 'metadata') + result = hatch("project", "metadata") assert result.exit_code == 1, result.output assert result.output == helpers.dedent( @@ -264,14 +264,14 @@ def test_incompatible_environment(hatch, temp_dir, helpers, build_env_config): def test_plugin_dependencies_unmet(hatch, temp_dir, helpers, mock_plugin_installation): - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() dependency = os.urandom(16).hex() @@ -284,15 +284,15 @@ def test_plugin_dependencies_unmet(hatch, temp_dir, helpers, mock_plugin_install ) ) - (path / 'README.md').replace(path / 'README.txt') + (path / "README.md").replace(path / "README.txt") project = Project(path) config = dict(project.raw_config) - config['project']['readme'] = 'README.txt' + config["project"]["readme"] = "README.txt" project.save_config(config) with path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('project', 'metadata') + result = hatch("project", "metadata") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -340,26 +340,26 @@ def test_plugin_dependencies_unmet(hatch, temp_dir, helpers, mock_plugin_install def test_build_dependencies_unmet(hatch, temp_dir, helpers): - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() - (path / 'README.md').replace(path / 'README.txt') + (path / "README.md").replace(path / "README.txt") project = Project(path) config = dict(project.raw_config) - config['project']['readme'] = 'README.txt' - config['tool']['hatch']['build'] = {'dependencies': ['binary']} + config["project"]["readme"] = "README.txt" + config["tool"]["hatch"]["build"] = {"dependencies": ["binary"]} project.save_config(config) with path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('project', 'metadata', 'license') + result = hatch("project", "metadata", "license") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -377,23 +377,23 @@ def test_build_dependencies_unmet(hatch, temp_dir, helpers): @pytest.mark.allow_backend_process @pytest.mark.requires_internet def test_no_compatibility_check_if_exists(hatch, temp_dir, helpers, mocker): - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() project = Project(project_path) config = dict(project.raw_config) - config['build-system']['requires'].append('binary') + config["build-system"]["requires"].append("binary") project.save_config(config) with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('project', 'metadata', 'license') + result = hatch("project", "metadata", "license") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -406,9 +406,9 @@ def test_no_compatibility_check_if_exists(hatch, temp_dir, helpers, mocker): """ ) - mocker.patch('hatch.env.virtual.VirtualEnvironment.check_compatibility', side_effect=Exception('incompatible')) + mocker.patch("hatch.env.virtual.VirtualEnvironment.check_compatibility", side_effect=Exception("incompatible")) with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('project', 'metadata', 'license') + result = hatch("project", "metadata", "license") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( diff --git a/tests/cli/publish/test_publish.py b/tests/cli/publish/test_publish.py index c23c7195f..f280fb374 100644 --- a/tests/cli/publish/test_publish.py +++ b/tests/cli/publish/test_publish.py @@ -11,8 +11,8 @@ pytestmark = [ pytest.mark.requires_docker, pytest.mark.requires_internet, - pytest.mark.usefixtures('devpi'), - pytest.mark.usefixtures('mock_backend_process'), + pytest.mark.usefixtures("devpi"), + pytest.mark.usefixtures("mock_backend_process"), ] @@ -20,11 +20,11 @@ def keyring_store(mocker): mock_store = defaultdict(dict) mocker.patch( - 'keyring.get_password', + "keyring.get_password", side_effect=lambda system, user: mock_store[system].get(user), ) mocker.patch( - 'keyring.set_password', + "keyring.set_password", side_effect=lambda system, user, auth: mock_store[system].__setitem__(user, auth), ) return mock_store @@ -32,13 +32,13 @@ def keyring_store(mocker): @pytest.fixture def published_project_name(): - return f'c4880cdbe05de9a28415fbad{secrets.choice(range(100))}' + return f"c4880cdbe05de9a28415fbad{secrets.choice(range(100))}" def remove_metadata_field(field: str, metadata_file_contents: str): lines = metadata_file_contents.splitlines(True) - field_marker = f'{field}: ' + field_marker = f"{field}: " indices_to_remove = [] for i, line in enumerate(lines): @@ -48,168 +48,168 @@ def remove_metadata_field(field: str, metadata_file_contents: str): for i, index in enumerate(indices_to_remove): del lines[index - i] - return ''.join(lines) + return "".join(lines) def timestamp_to_version(timestamp): - major, minor = str(timestamp).split('.') - if minor.startswith('0'): + major, minor = str(timestamp).split(".") + if minor.startswith("0"): normalized_minor = str(int(minor)) - padding = '.'.join('0' for _ in range(len(minor) - len(normalized_minor))) - return f'{major}.{padding}.{normalized_minor}' + padding = ".".join("0" for _ in range(len(minor) - len(normalized_minor))) + return f"{major}.{padding}.{normalized_minor}" - return f'{major}.{minor}' + return f"{major}.{minor}" def test_timestamp_to_version(): - assert timestamp_to_version(123.4) == '123.4' - assert timestamp_to_version(123.04) == '123.0.4' - assert timestamp_to_version(123.004) == '123.0.0.4' + assert timestamp_to_version(123.4) == "123.4" + assert timestamp_to_version(123.04) == "123.0.4" + assert timestamp_to_version(123.004) == "123.0.0.4" def test_explicit_options(hatch, temp_dir): - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - path = temp_dir / 'my-app' + path = temp_dir / "my-app" with path.as_cwd(): - result = hatch('publish', '-o', 'foo=bar') + result = hatch("publish", "-o", "foo=bar") assert result.exit_code == 1, result.output assert result.output == ( - 'Use the standard CLI flags rather than passing explicit options when using the `index` plugin\n' + "Use the standard CLI flags rather than passing explicit options when using the `index` plugin\n" ) def test_unknown_publisher(hatch, temp_dir): - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - path = temp_dir / 'my-app' + path = temp_dir / "my-app" with path.as_cwd(): - result = hatch('publish', '-p', 'foo') + result = hatch("publish", "-p", "foo") assert result.exit_code == 1, result.output - assert result.output == 'Unknown publisher: foo\n' + assert result.output == "Unknown publisher: foo\n" def test_disabled(hatch, temp_dir, config_file): - config_file.model.publish['index']['disable'] = True + config_file.model.publish["index"]["disable"] = True config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - path = temp_dir / 'my-app' + path = temp_dir / "my-app" with path.as_cwd(): - result = hatch('publish', '-n') + result = hatch("publish", "-n") assert result.exit_code == 1, result.output - assert result.output == 'Publisher is disabled: index\n' + assert result.output == "Publisher is disabled: index\n" def test_repo_invalid_type(hatch, temp_dir, config_file): - config_file.model.publish['index']['repos'] = {'dev': 9000} + config_file.model.publish["index"]["repos"] = {"dev": 9000} config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - path = temp_dir / 'my-app' + path = temp_dir / "my-app" with path.as_cwd(): - result = hatch('publish', '--user', 'foo', '--auth', 'bar') + result = hatch("publish", "--user", "foo", "--auth", "bar") assert result.exit_code == 1, result.output - assert result.output == 'Hatch config field `publish.index.repos.dev` must be a string or a mapping\n' + assert result.output == "Hatch config field `publish.index.repos.dev` must be a string or a mapping\n" def test_repo_missing_url(hatch, temp_dir, config_file): - config_file.model.publish['index']['repos'] = {'dev': {}} + config_file.model.publish["index"]["repos"] = {"dev": {}} config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - path = temp_dir / 'my-app' + path = temp_dir / "my-app" with path.as_cwd(): - result = hatch('publish', '--user', 'foo', '--auth', 'bar') + result = hatch("publish", "--user", "foo", "--auth", "bar") assert result.exit_code == 1, result.output - assert result.output == 'Hatch config field `publish.index.repos.dev` must define a `url` key\n' + assert result.output == "Hatch config field `publish.index.repos.dev` must define a `url` key\n" def test_missing_user(hatch, temp_dir): - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - path = temp_dir / 'my-app' + path = temp_dir / "my-app" with path.as_cwd(): - result = hatch('publish', '-n') + result = hatch("publish", "-n") assert result.exit_code == 1, result.output - assert result.output == 'Missing required option: user\n' + assert result.output == "Missing required option: user\n" def test_missing_auth(hatch, temp_dir): - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - path = temp_dir / 'my-app' + path = temp_dir / "my-app" with path.as_cwd(): - result = hatch('publish', '-n', '--user', 'foo') + result = hatch("publish", "-n", "--user", "foo") assert result.exit_code == 1, result.output - assert result.output == 'Missing required option: auth\n' + assert result.output == "Missing required option: auth\n" def test_flags(hatch, devpi, temp_dir_cache, helpers, published_project_name): with temp_dir_cache.as_cwd(): - result = hatch('new', published_project_name) + result = hatch("new", published_project_name) assert result.exit_code == 0, result.output path = temp_dir_cache / published_project_name with path.as_cwd(): current_version = timestamp_to_version(helpers.get_current_timestamp()) - result = hatch('version', current_version) + result = hatch("version", current_version) assert result.exit_code == 0, result.output - result = hatch('build') + result = hatch("build") assert result.exit_code == 0, result.output - build_directory = path / 'dist' + build_directory = path / "dist" artifacts = list(build_directory.iterdir()) result = hatch( - 'publish', '--repo', devpi.repo, '--user', devpi.user, '--auth', devpi.auth, '--ca-cert', devpi.ca_cert + "publish", "--repo", devpi.repo, "--user", devpi.user, "--auth", devpi.auth, "--ca-cert", devpi.ca_cert ) assert result.exit_code == 0, result.output @@ -225,15 +225,15 @@ def test_flags(hatch, devpi, temp_dir_cache, helpers, published_project_name): def test_plugin_config(hatch, devpi, temp_dir_cache, helpers, published_project_name, config_file): - config_file.model.publish['index']['user'] = devpi.user - config_file.model.publish['index']['auth'] = devpi.auth - config_file.model.publish['index']['ca-cert'] = devpi.ca_cert - config_file.model.publish['index']['repo'] = 'dev' - config_file.model.publish['index']['repos'] = {'dev': devpi.repo} + config_file.model.publish["index"]["user"] = devpi.user + config_file.model.publish["index"]["auth"] = devpi.auth + config_file.model.publish["index"]["ca-cert"] = devpi.ca_cert + config_file.model.publish["index"]["repo"] = "dev" + config_file.model.publish["index"]["repos"] = {"dev": devpi.repo} config_file.save() with temp_dir_cache.as_cwd(): - result = hatch('new', published_project_name) + result = hatch("new", published_project_name) assert result.exit_code == 0, result.output path = temp_dir_cache / published_project_name @@ -242,16 +242,16 @@ def test_plugin_config(hatch, devpi, temp_dir_cache, helpers, published_project_ del os.environ[PublishEnvVars.REPO] current_version = timestamp_to_version(helpers.get_current_timestamp()) - result = hatch('version', current_version) + result = hatch("version", current_version) assert result.exit_code == 0, result.output - result = hatch('build') + result = hatch("build") assert result.exit_code == 0, result.output - build_directory = path / 'dist' + build_directory = path / "dist" artifacts = list(build_directory.iterdir()) - result = hatch('publish') + result = hatch("publish") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -266,17 +266,17 @@ def test_plugin_config(hatch, devpi, temp_dir_cache, helpers, published_project_ def test_plugin_config_repo_override(hatch, devpi, temp_dir_cache, helpers, published_project_name, config_file): - config_file.model.publish['index']['user'] = 'foo' - config_file.model.publish['index']['auth'] = 'bar' - config_file.model.publish['index']['ca-cert'] = 'cert' - config_file.model.publish['index']['repo'] = 'dev' - config_file.model.publish['index']['repos'] = { - 'dev': {'url': devpi.repo, 'user': devpi.user, 'auth': devpi.auth, 'ca-cert': devpi.ca_cert}, + config_file.model.publish["index"]["user"] = "foo" + config_file.model.publish["index"]["auth"] = "bar" + config_file.model.publish["index"]["ca-cert"] = "cert" + config_file.model.publish["index"]["repo"] = "dev" + config_file.model.publish["index"]["repos"] = { + "dev": {"url": devpi.repo, "user": devpi.user, "auth": devpi.auth, "ca-cert": devpi.ca_cert}, } config_file.save() with temp_dir_cache.as_cwd(): - result = hatch('new', published_project_name) + result = hatch("new", published_project_name) assert result.exit_code == 0, result.output path = temp_dir_cache / published_project_name @@ -285,16 +285,16 @@ def test_plugin_config_repo_override(hatch, devpi, temp_dir_cache, helpers, publ del os.environ[PublishEnvVars.REPO] current_version = timestamp_to_version(helpers.get_current_timestamp()) - result = hatch('version', current_version) + result = hatch("version", current_version) assert result.exit_code == 0, result.output - result = hatch('build') + result = hatch("build") assert result.exit_code == 0, result.output - build_directory = path / 'dist' + build_directory = path / "dist" artifacts = list(build_directory.iterdir()) - result = hatch('publish') + result = hatch("publish") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -309,50 +309,50 @@ def test_plugin_config_repo_override(hatch, devpi, temp_dir_cache, helpers, publ def test_prompt(hatch, devpi, temp_dir_cache, helpers, published_project_name, config_file): - config_file.model.publish['index']['ca-cert'] = devpi.ca_cert - config_file.model.publish['index']['repo'] = 'dev' - config_file.model.publish['index']['repos'] = {'dev': devpi.repo} + config_file.model.publish["index"]["ca-cert"] = devpi.ca_cert + config_file.model.publish["index"]["repo"] = "dev" + config_file.model.publish["index"]["repos"] = {"dev": devpi.repo} config_file.save() with temp_dir_cache.as_cwd(): - result = hatch('new', published_project_name) + result = hatch("new", published_project_name) assert result.exit_code == 0, result.output path = temp_dir_cache / published_project_name with path.as_cwd(): current_version = timestamp_to_version(helpers.get_current_timestamp()) - result = hatch('version', current_version) + result = hatch("version", current_version) assert result.exit_code == 0, result.output - result = hatch('build') + result = hatch("build") assert result.exit_code == 0, result.output - build_directory = path / 'dist' + build_directory = path / "dist" artifacts = list(build_directory.iterdir()) - result = hatch('publish', input=f'{devpi.user}\nfoo') + result = hatch("publish", input=f"{devpi.user}\nfoo") assert result.exit_code == 1, result.output - assert '401' in result.output - assert 'Unauthorized' in result.output + assert "401" in result.output + assert "Unauthorized" in result.output # Ensure nothing is saved for errors with path.as_cwd(): - result = hatch('publish', '-n') + result = hatch("publish", "-n") assert result.exit_code == 1, result.output - assert result.output == 'Missing required option: user\n' + assert result.output == "Missing required option: user\n" # Trigger save with path.as_cwd(): - result = hatch('publish', str(artifacts[0]), input=f'{devpi.user}\n{devpi.auth}') + result = hatch("publish", str(artifacts[0]), input=f"{devpi.user}\n{devpi.auth}") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( f""" Username for '{devpi.repo}' [__token__]: {devpi.user} - Password / Token:{' '} + Password / Token:{" "} {artifacts[0].relative_to(path)} ... success [{published_project_name}] @@ -362,7 +362,7 @@ def test_prompt(hatch, devpi, temp_dir_cache, helpers, published_project_name, c # Use saved results with path.as_cwd(): - result = hatch('publish', str(artifacts[1])) + result = hatch("publish", str(artifacts[1])) assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -376,43 +376,43 @@ def test_prompt(hatch, devpi, temp_dir_cache, helpers, published_project_name, c def test_initialize_auth(hatch, devpi, temp_dir_cache, helpers, published_project_name, config_file): - config_file.model.publish['index']['ca-cert'] = devpi.ca_cert - config_file.model.publish['index']['repo'] = 'dev' - config_file.model.publish['index']['repos'] = {'dev': devpi.repo} + config_file.model.publish["index"]["ca-cert"] = devpi.ca_cert + config_file.model.publish["index"]["repo"] = "dev" + config_file.model.publish["index"]["repos"] = {"dev": devpi.repo} config_file.save() with temp_dir_cache.as_cwd(): - result = hatch('new', published_project_name) + result = hatch("new", published_project_name) assert result.exit_code == 0, result.output path = temp_dir_cache / published_project_name # Trigger save with path.as_cwd(): - result = hatch('publish', '--initialize-auth', input=f'{devpi.user}\n{devpi.auth}') + result = hatch("publish", "--initialize-auth", input=f"{devpi.user}\n{devpi.auth}") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( f""" Username for '{devpi.repo}' [__token__]: {devpi.user} - Password / Token:{' '} + Password / Token:{" "} """ ) with path.as_cwd(): current_version = timestamp_to_version(helpers.get_current_timestamp()) - result = hatch('version', current_version) + result = hatch("version", current_version) assert result.exit_code == 0, result.output - result = hatch('build', '-t', 'wheel') + result = hatch("build", "-t", "wheel") assert result.exit_code == 0, result.output - build_directory = path / 'dist' + build_directory = path / "dist" artifacts = list(build_directory.iterdir()) # Use saved results with path.as_cwd(): - result = hatch('publish', str(artifacts[0])) + result = hatch("publish", str(artifacts[0])) assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -426,35 +426,35 @@ def test_initialize_auth(hatch, devpi, temp_dir_cache, helpers, published_projec def test_external_artifact_path(hatch, devpi, temp_dir_cache, helpers, published_project_name, config_file): - config_file.model.publish['index']['ca-cert'] = devpi.ca_cert - config_file.model.publish['index']['repo'] = 'dev' - config_file.model.publish['index']['repos'] = {'dev': devpi.repo} + config_file.model.publish["index"]["ca-cert"] = devpi.ca_cert + config_file.model.publish["index"]["repo"] = "dev" + config_file.model.publish["index"]["repos"] = {"dev": devpi.repo} config_file.save() with temp_dir_cache.as_cwd(): - result = hatch('new', published_project_name) + result = hatch("new", published_project_name) assert result.exit_code == 0, result.output path = temp_dir_cache / published_project_name - external_build_directory = temp_dir_cache / 'dist' + external_build_directory = temp_dir_cache / "dist" with path.as_cwd(): current_version = timestamp_to_version(helpers.get_current_timestamp()) - result = hatch('version', current_version) + result = hatch("version", current_version) assert result.exit_code == 0, result.output - result = hatch('build', '-t', 'sdist', str(external_build_directory)) + result = hatch("build", "-t", "sdist", str(external_build_directory)) assert result.exit_code == 0, result.output external_artifacts = list(external_build_directory.iterdir()) - result = hatch('build', '-t', 'wheel') + result = hatch("build", "-t", "wheel") assert result.exit_code == 0, result.output - internal_build_directory = path / 'dist' + internal_build_directory = path / "dist" internal_artifacts = list(internal_build_directory.iterdir()) - result = hatch('publish', '--user', devpi.user, '--auth', devpi.auth, 'dist', str(external_build_directory)) + result = hatch("publish", "--user", devpi.user, "--auth", devpi.auth, "dist", str(external_build_directory)) assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -469,29 +469,29 @@ def test_external_artifact_path(hatch, devpi, temp_dir_cache, helpers, published def test_already_exists(hatch, devpi, temp_dir_cache, helpers, published_project_name, config_file): - config_file.model.publish['index']['ca-cert'] = devpi.ca_cert - config_file.model.publish['index']['repo'] = 'dev' - config_file.model.publish['index']['repos'] = {'dev': devpi.repo} + config_file.model.publish["index"]["ca-cert"] = devpi.ca_cert + config_file.model.publish["index"]["repo"] = "dev" + config_file.model.publish["index"]["repos"] = {"dev": devpi.repo} config_file.save() with temp_dir_cache.as_cwd(): - result = hatch('new', published_project_name) + result = hatch("new", published_project_name) assert result.exit_code == 0, result.output path = temp_dir_cache / published_project_name with path.as_cwd(): current_version = timestamp_to_version(helpers.get_current_timestamp()) - result = hatch('version', current_version) + result = hatch("version", current_version) assert result.exit_code == 0, result.output - result = hatch('build') + result = hatch("build") assert result.exit_code == 0, result.output - build_directory = path / 'dist' + build_directory = path / "dist" artifacts = list(build_directory.iterdir()) - result = hatch('publish', '--user', devpi.user, '--auth', devpi.auth) + result = hatch("publish", "--user", devpi.user, "--auth", devpi.auth) assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -505,7 +505,7 @@ def test_already_exists(hatch, devpi, temp_dir_cache, helpers, published_project ) with path.as_cwd(): - result = hatch('publish', '--user', devpi.user, '--auth', devpi.auth) + result = hatch("publish", "--user", devpi.user, "--auth", devpi.auth) assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -518,17 +518,17 @@ def test_already_exists(hatch, devpi, temp_dir_cache, helpers, published_project def test_no_artifacts(hatch, temp_dir_cache, helpers, published_project_name): with temp_dir_cache.as_cwd(): - result = hatch('new', published_project_name) + result = hatch("new", published_project_name) assert result.exit_code == 0, result.output path = temp_dir_cache / published_project_name with path.as_cwd(): - directory = path / 'dir2' + directory = path / "dir2" directory.mkdir() - (directory / 'test.txt').touch() + (directory / "test.txt").touch() - result = hatch('publish', 'dir1', 'dir2', '--user', 'foo', '--auth', 'bar') + result = hatch("publish", "dir1", "dir2", "--user", "foo", "--auth", "bar") assert result.exit_code == 1, result.output assert result.output == helpers.dedent( @@ -539,16 +539,16 @@ def test_no_artifacts(hatch, temp_dir_cache, helpers, published_project_name): def test_enable_with_flag(hatch, devpi, temp_dir_cache, helpers, published_project_name, config_file): - config_file.model.publish['index']['user'] = devpi.user - config_file.model.publish['index']['auth'] = devpi.auth - config_file.model.publish['index']['ca-cert'] = devpi.ca_cert - config_file.model.publish['index']['repo'] = 'dev' - config_file.model.publish['index']['repos'] = {'dev': devpi.repo} - config_file.model.publish['index']['disable'] = True + config_file.model.publish["index"]["user"] = devpi.user + config_file.model.publish["index"]["auth"] = devpi.auth + config_file.model.publish["index"]["ca-cert"] = devpi.ca_cert + config_file.model.publish["index"]["repo"] = "dev" + config_file.model.publish["index"]["repos"] = {"dev": devpi.repo} + config_file.model.publish["index"]["disable"] = True config_file.save() with temp_dir_cache.as_cwd(): - result = hatch('new', published_project_name) + result = hatch("new", published_project_name) assert result.exit_code == 0, result.output path = temp_dir_cache / published_project_name @@ -557,16 +557,16 @@ def test_enable_with_flag(hatch, devpi, temp_dir_cache, helpers, published_proje del os.environ[PublishEnvVars.REPO] current_version = timestamp_to_version(helpers.get_current_timestamp()) - result = hatch('version', current_version) + result = hatch("version", current_version) assert result.exit_code == 0, result.output - result = hatch('build') + result = hatch("build") assert result.exit_code == 0, result.output - build_directory = path / 'dist' + build_directory = path / "dist" artifacts = list(build_directory.iterdir()) - result = hatch('publish', '-y') + result = hatch("publish", "-y") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -581,16 +581,16 @@ def test_enable_with_flag(hatch, devpi, temp_dir_cache, helpers, published_proje def test_enable_with_prompt(hatch, devpi, temp_dir_cache, helpers, published_project_name, config_file): - config_file.model.publish['index']['user'] = devpi.user - config_file.model.publish['index']['auth'] = devpi.auth - config_file.model.publish['index']['ca-cert'] = devpi.ca_cert - config_file.model.publish['index']['repo'] = 'dev' - config_file.model.publish['index']['repos'] = {'dev': devpi.repo} - config_file.model.publish['index']['disable'] = True + config_file.model.publish["index"]["user"] = devpi.user + config_file.model.publish["index"]["auth"] = devpi.auth + config_file.model.publish["index"]["ca-cert"] = devpi.ca_cert + config_file.model.publish["index"]["repo"] = "dev" + config_file.model.publish["index"]["repos"] = {"dev": devpi.repo} + config_file.model.publish["index"]["disable"] = True config_file.save() with temp_dir_cache.as_cwd(): - result = hatch('new', published_project_name) + result = hatch("new", published_project_name) assert result.exit_code == 0, result.output path = temp_dir_cache / published_project_name @@ -599,16 +599,16 @@ def test_enable_with_prompt(hatch, devpi, temp_dir_cache, helpers, published_pro del os.environ[PublishEnvVars.REPO] current_version = timestamp_to_version(helpers.get_current_timestamp()) - result = hatch('version', current_version) + result = hatch("version", current_version) assert result.exit_code == 0, result.output - result = hatch('build') + result = hatch("build") assert result.exit_code == 0, result.output - build_directory = path / 'dist' + build_directory = path / "dist" artifacts = list(build_directory.iterdir()) - result = hatch('publish', input='y\n') + result = hatch("publish", input="y\n") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -624,38 +624,39 @@ def test_enable_with_prompt(hatch, devpi, temp_dir_cache, helpers, published_pro class TestWheel: - @pytest.mark.parametrize('field', ['name', 'version']) + @pytest.mark.parametrize("field", ["name", "version"]) def test_missing_required_metadata_field(self, hatch, temp_dir_cache, helpers, published_project_name, field): with temp_dir_cache.as_cwd(): - result = hatch('new', published_project_name) + result = hatch("new", published_project_name) assert result.exit_code == 0, result.output path = temp_dir_cache / published_project_name with path.as_cwd(): current_version = timestamp_to_version(helpers.get_current_timestamp()) - result = hatch('version', current_version) + result = hatch("version", current_version) assert result.exit_code == 0, result.output - result = hatch('build', '-t', 'wheel') + result = hatch("build", "-t", "wheel") assert result.exit_code == 0, result.output - build_directory = path / 'dist' + build_directory = path / "dist" artifacts = list(build_directory.iterdir()) artifact_path = str(artifacts[0]) - metadata_file_path = f'{published_project_name}-{current_version}.dist-info/METADATA' + metadata_file_path = f"{published_project_name}-{current_version}.dist-info/METADATA" - with zipfile.ZipFile(artifact_path, 'r') as zip_archive, zip_archive.open(metadata_file_path) as metadata_file: - metadata_file_contents = metadata_file.read().decode('utf-8') + with zipfile.ZipFile(artifact_path, "r") as zip_archive, zip_archive.open(metadata_file_path) as metadata_file: + metadata_file_contents = metadata_file.read().decode("utf-8") - with zipfile.ZipFile(artifact_path, 'w') as zip_archive, zip_archive.open( - metadata_file_path, 'w' - ) as metadata_file: - metadata_file.write(remove_metadata_field(field, metadata_file_contents).encode('utf-8')) + with ( + zipfile.ZipFile(artifact_path, "w") as zip_archive, + zip_archive.open(metadata_file_path, "w") as metadata_file, + ): + metadata_file.write(remove_metadata_field(field, metadata_file_contents).encode("utf-8")) with path.as_cwd(): - result = hatch('publish', '--user', 'foo', '--auth', 'bar') + result = hatch("publish", "--user", "foo", "--auth", "bar") assert result.exit_code == 1, result.output assert result.output == helpers.dedent( @@ -666,39 +667,39 @@ def test_missing_required_metadata_field(self, hatch, temp_dir_cache, helpers, p class TestSourceDistribution: - @pytest.mark.parametrize('field', ['name', 'version']) + @pytest.mark.parametrize("field", ["name", "version"]) def test_missing_required_metadata_field(self, hatch, temp_dir_cache, helpers, published_project_name, field): with temp_dir_cache.as_cwd(): - result = hatch('new', published_project_name) + result = hatch("new", published_project_name) assert result.exit_code == 0, result.output path = temp_dir_cache / published_project_name with path.as_cwd(): current_version = timestamp_to_version(helpers.get_current_timestamp()) - result = hatch('version', current_version) + result = hatch("version", current_version) assert result.exit_code == 0, result.output - result = hatch('build', '-t', 'sdist') + result = hatch("build", "-t", "sdist") assert result.exit_code == 0, result.output - build_directory = path / 'dist' + build_directory = path / "dist" artifacts = list(build_directory.iterdir()) artifact_path = str(artifacts[0]) - extraction_directory = path / 'extraction' + extraction_directory = path / "extraction" - with tarfile.open(artifact_path, 'r:gz') as tar_archive: + with tarfile.open(artifact_path, "r:gz") as tar_archive: tar_archive.extractall(extraction_directory, **helpers.tarfile_extraction_compat_options()) - metadata_file_path = extraction_directory / f'{published_project_name}-{current_version}' / 'PKG-INFO' + metadata_file_path = extraction_directory / f"{published_project_name}-{current_version}" / "PKG-INFO" metadata_file_path.write_text(remove_metadata_field(field, metadata_file_path.read_text())) - with tarfile.open(artifact_path, 'w:gz') as tar_archive: - tar_archive.add(extraction_directory, arcname='') + with tarfile.open(artifact_path, "w:gz") as tar_archive: + tar_archive.add(extraction_directory, arcname="") with path.as_cwd(): - result = hatch('publish', '--user', 'foo', '--auth', 'bar') + result = hatch("publish", "--user", "foo", "--auth", "bar") assert result.exit_code == 1, result.output assert result.output == helpers.dedent( diff --git a/tests/cli/python/conftest.py b/tests/cli/python/conftest.py index af4c66b28..1f4672a59 100644 --- a/tests/cli/python/conftest.py +++ b/tests/cli/python/conftest.py @@ -12,19 +12,19 @@ def default_shells(platform): @pytest.fixture(autouse=True) def isolated_python_directory(config_file): - config_file.model.dirs.python = 'isolated' + config_file.model.dirs.python = "isolated" config_file.save() @pytest.fixture(autouse=True) def path_append(mocker): - return mocker.patch('userpath.append') + return mocker.patch("userpath.append") @pytest.fixture(autouse=True) def disable_path_detectors(mocker): - mocker.patch('userpath.in_current_path', return_value=False) - mocker.patch('userpath.in_new_path', return_value=False) + mocker.patch("userpath.in_current_path", return_value=False) + mocker.patch("userpath.in_new_path", return_value=False) @pytest.fixture diff --git a/tests/cli/python/test_find.py b/tests/cli/python/test_find.py index aa03418c4..bde65dff7 100644 --- a/tests/cli/python/test_find.py +++ b/tests/cli/python/test_find.py @@ -1,6 +1,6 @@ def test_not_installed(hatch, helpers): - name = '3.10' - result = hatch('python', 'find', name) + name = "3.10" + result = hatch("python", "find", name) assert result.exit_code == 1, result.output assert result.output == helpers.dedent( @@ -11,10 +11,10 @@ def test_not_installed(hatch, helpers): def test_binary(hatch, helpers, temp_dir_data, dist_name): - install_dir = temp_dir_data / 'data' / 'pythons' + install_dir = temp_dir_data / "data" / "pythons" dist = helpers.write_distribution(install_dir, dist_name) - result = hatch('python', 'find', dist_name) + result = hatch("python", "find", dist_name) assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -25,10 +25,10 @@ def test_binary(hatch, helpers, temp_dir_data, dist_name): def test_parent(hatch, helpers, temp_dir_data, dist_name): - install_dir = temp_dir_data / 'data' / 'pythons' + install_dir = temp_dir_data / "data" / "pythons" dist = helpers.write_distribution(install_dir, dist_name) - result = hatch('python', 'find', dist_name, '--parent') + result = hatch("python", "find", dist_name, "--parent") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( diff --git a/tests/cli/python/test_install.py b/tests/cli/python/test_install.py index f8a462771..e9656d682 100644 --- a/tests/cli/python/test_install.py +++ b/tests/cli/python/test_install.py @@ -10,9 +10,9 @@ def test_unknown(hatch, helpers, path_append, mocker): - install = mocker.patch('hatch.python.core.PythonManager.install') + install = mocker.patch("hatch.python.core.PythonManager.install") - result = hatch('python', 'install', 'foo', 'bar') + result = hatch("python", "install", "foo", "bar") assert result.exit_code == 1, result.output assert result.output == helpers.dedent( @@ -26,10 +26,10 @@ def test_unknown(hatch, helpers, path_append, mocker): def test_incompatible_single(hatch, helpers, path_append, dist_name, mocker): - mocker.patch('hatch.python.resolve.get_distribution', side_effect=PythonDistributionResolutionError) - install = mocker.patch('hatch.python.core.PythonManager.install') + mocker.patch("hatch.python.resolve.get_distribution", side_effect=PythonDistributionResolutionError) + install = mocker.patch("hatch.python.core.PythonManager.install") - result = hatch('python', 'install', dist_name) + result = hatch("python", "install", dist_name) assert result.exit_code == 1, result.output assert result.output == helpers.dedent( @@ -43,15 +43,15 @@ def test_incompatible_single(hatch, helpers, path_append, dist_name, mocker): def test_incompatible_all(hatch, helpers, path_append, mocker): - mocker.patch('hatch.python.resolve.get_distribution', side_effect=PythonDistributionResolutionError) - install = mocker.patch('hatch.python.core.PythonManager.install') + mocker.patch("hatch.python.resolve.get_distribution", side_effect=PythonDistributionResolutionError) + install = mocker.patch("hatch.python.core.PythonManager.install") - result = hatch('python', 'install', 'all') + result = hatch("python", "install", "all") assert result.exit_code == 1, result.output assert result.output == helpers.dedent( f""" - Incompatible distributions: {', '.join(ORDERED_DISTRIBUTIONS)} + Incompatible distributions: {", ".join(ORDERED_DISTRIBUTIONS)} """ ) @@ -63,13 +63,13 @@ def test_incompatible_all(hatch, helpers, path_append, mocker): def test_installation( hatch, helpers, temp_dir_data, platform, path_append, default_shells, compatible_python_distributions ): - selection = [name for name in compatible_python_distributions if not name.startswith('pypy')] + selection = [name for name in compatible_python_distributions if not name.startswith("pypy")] dist_name = secrets.choice(selection) - result = hatch('python', 'install', dist_name) + result = hatch("python", "install", dist_name) - install_dir = temp_dir_data / 'data' / 'pythons' / dist_name + install_dir = temp_dir_data / "data" / "pythons" / dist_name metadata_file = install_dir / InstalledDistribution.metadata_filename() - python_path = install_dir / json.loads(metadata_file.read_text())['python_path'] + python_path = install_dir / json.loads(metadata_file.read_text())["python_path"] assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -85,21 +85,21 @@ def test_installation( assert python_path.is_file() - output = platform.check_command_output([python_path, '-c', 'import sys;print(sys.executable)']).strip() + output = platform.check_command_output([python_path, "-c", "import sys;print(sys.executable)"]).strip() assert output == str(python_path) - output = platform.check_command_output([python_path, '--version']).strip() - assert output.startswith(f'Python {dist_name}.') + output = platform.check_command_output([python_path, "--version"]).strip() + assert output.startswith(f"Python {dist_name}.") path_append.assert_called_once_with(str(python_path.parent), shells=default_shells) def test_already_installed_latest(hatch, helpers, temp_dir_data, path_append, dist_name, mocker): - install = mocker.patch('hatch.python.core.PythonManager.install') - install_dir = temp_dir_data / 'data' / 'pythons' + install = mocker.patch("hatch.python.core.PythonManager.install") + install_dir = temp_dir_data / "data" / "pythons" installed_dist = helpers.write_distribution(install_dir, dist_name) - result = hatch('python', 'install', dist_name) + result = hatch("python", "install", dist_name) assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -113,12 +113,12 @@ def test_already_installed_latest(hatch, helpers, temp_dir_data, path_append, di def test_already_installed_update_disabled(hatch, helpers, temp_dir_data, path_append, dist_name, mocker): - install = mocker.patch('hatch.python.core.PythonManager.install') - install_dir = temp_dir_data / 'data' / 'pythons' + install = mocker.patch("hatch.python.core.PythonManager.install") + install_dir = temp_dir_data / "data" / "pythons" helpers.write_distribution(install_dir, dist_name) helpers.downgrade_distribution_metadata(install_dir / dist_name) - result = hatch('python', 'install', dist_name, input='n\n') + result = hatch("python", "install", dist_name, input="n\n") assert result.exit_code == 1, result.output assert result.output == helpers.dedent( @@ -133,17 +133,17 @@ def test_already_installed_update_disabled(hatch, helpers, temp_dir_data, path_a def test_already_installed_update_prompt(hatch, helpers, temp_dir_data, path_append, default_shells, dist_name, mocker): - install_dir = temp_dir_data / 'data' / 'pythons' + install_dir = temp_dir_data / "data" / "pythons" helpers.write_distribution(install_dir, dist_name) dist_dir = install_dir / dist_name metadata = helpers.downgrade_distribution_metadata(dist_dir) - python_path = dist_dir / metadata['python_path'] + python_path = dist_dir / metadata["python_path"] install = mocker.patch( - 'hatch.python.core.PythonManager.install', return_value=mocker.MagicMock(path=dist_dir, python_path=python_path) + "hatch.python.core.PythonManager.install", return_value=mocker.MagicMock(path=dist_dir, python_path=python_path) ) - result = hatch('python', 'install', dist_name, input='y\n') + result = hatch("python", "install", dist_name, input="y\n") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -163,17 +163,17 @@ def test_already_installed_update_prompt(hatch, helpers, temp_dir_data, path_app def test_already_installed_update_flag(hatch, helpers, temp_dir_data, path_append, default_shells, dist_name, mocker): - install_dir = temp_dir_data / 'data' / 'pythons' + install_dir = temp_dir_data / "data" / "pythons" helpers.write_distribution(install_dir, dist_name) dist_dir = install_dir / dist_name metadata = helpers.downgrade_distribution_metadata(dist_dir) - python_path = dist_dir / metadata['python_path'] + python_path = dist_dir / metadata["python_path"] install = mocker.patch( - 'hatch.python.core.PythonManager.install', return_value=mocker.MagicMock(path=dist_dir, python_path=python_path) + "hatch.python.core.PythonManager.install", return_value=mocker.MagicMock(path=dist_dir, python_path=python_path) ) - result = hatch('python', 'install', '--update', dist_name) + result = hatch("python", "install", "--update", dist_name) assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -191,16 +191,16 @@ def test_already_installed_update_flag(hatch, helpers, temp_dir_data, path_appen path_append.assert_called_once_with(str(python_path.parent), shells=default_shells) -@pytest.mark.parametrize('detector', ['in_current_path', 'in_new_path']) +@pytest.mark.parametrize("detector", ["in_current_path", "in_new_path"]) def test_already_in_path(hatch, helpers, temp_dir_data, path_append, mocker, detector, dist_name): - mocker.patch(f'userpath.{detector}', return_value=True) - dist_dir = temp_dir_data / 'data' / 'pythons' / dist_name + mocker.patch(f"userpath.{detector}", return_value=True) + dist_dir = temp_dir_data / "data" / "pythons" / dist_name python_path = dist_dir / get_distribution(dist_name).python_path install = mocker.patch( - 'hatch.python.core.PythonManager.install', return_value=mocker.MagicMock(path=dist_dir, python_path=python_path) + "hatch.python.core.PythonManager.install", return_value=mocker.MagicMock(path=dist_dir, python_path=python_path) ) - result = hatch('python', 'install', dist_name) + result = hatch("python", "install", dist_name) assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -215,13 +215,13 @@ def test_already_in_path(hatch, helpers, temp_dir_data, path_append, mocker, det def test_private(hatch, helpers, temp_dir_data, path_append, dist_name, mocker): - dist_dir = temp_dir_data / 'data' / 'pythons' / dist_name + dist_dir = temp_dir_data / "data" / "pythons" / dist_name python_path = dist_dir / get_distribution(dist_name).python_path install = mocker.patch( - 'hatch.python.core.PythonManager.install', return_value=mocker.MagicMock(path=dist_dir, python_path=python_path) + "hatch.python.core.PythonManager.install", return_value=mocker.MagicMock(path=dist_dir, python_path=python_path) ) - result = hatch('python', 'install', '--private', dist_name) + result = hatch("python", "install", "--private", dist_name) assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -236,14 +236,14 @@ def test_private(hatch, helpers, temp_dir_data, path_append, dist_name, mocker): def test_specific_location(hatch, helpers, temp_dir_data, path_append, dist_name, mocker): - install_dir = temp_dir_data / 'foo' / 'bar' / 'baz' + install_dir = temp_dir_data / "foo" / "bar" / "baz" dist_dir = install_dir / dist_name python_path = dist_dir / get_distribution(dist_name).python_path install = mocker.patch( - 'hatch.python.core.PythonManager.install', return_value=mocker.MagicMock(path=dist_dir, python_path=python_path) + "hatch.python.core.PythonManager.install", return_value=mocker.MagicMock(path=dist_dir, python_path=python_path) ) - result = hatch('python', 'install', '--private', '-d', str(install_dir), dist_name) + result = hatch("python", "install", "--private", "-d", str(install_dir), dist_name) assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -260,29 +260,29 @@ def test_specific_location(hatch, helpers, temp_dir_data, path_append, dist_name def test_all(hatch, temp_dir_data, path_append, default_shells, mocker, compatible_python_distributions): mocked_dists = [] for name in compatible_python_distributions: - dist_dir = temp_dir_data / 'data' / 'pythons' / name + dist_dir = temp_dir_data / "data" / "pythons" / name python_path = dist_dir / get_distribution(name).python_path mocked_dists.append(mocker.MagicMock(path=dist_dir, python_path=python_path)) - install = mocker.patch('hatch.python.core.PythonManager.install', side_effect=mocked_dists) + install = mocker.patch("hatch.python.core.PythonManager.install", side_effect=mocked_dists) - result = hatch('python', 'install', 'all') + result = hatch("python", "install", "all") assert result.exit_code == 0, result.output expected_lines = [] for dist in mocked_dists: - expected_lines.extend((f'Installing {dist.path.name}', f'Installed {dist.path.name} @ {dist.path}')) + expected_lines.extend((f"Installing {dist.path.name}", f"Installed {dist.path.name} @ {dist.path}")) expected_lines.extend(( - '', - 'The following directories have been added to your PATH (pending a shell restart):', - '', + "", + "The following directories have been added to your PATH (pending a shell restart):", + "", )) expected_lines.extend(str(dist.python_path.parent) for dist in mocked_dists) - expected_lines.append('') + expected_lines.append("") - assert result.output == '\n'.join(expected_lines) + assert result.output == "\n".join(expected_lines) assert install.call_args_list == [mocker.call(name) for name in compatible_python_distributions] assert path_append.call_args_list == [ diff --git a/tests/cli/python/test_remove.py b/tests/cli/python/test_remove.py index ea1ca2248..77d66f480 100644 --- a/tests/cli/python/test_remove.py +++ b/tests/cli/python/test_remove.py @@ -1,5 +1,5 @@ def test_not_installed(hatch, helpers): - result = hatch('python', 'remove', '3.9', '3.10') + result = hatch("python", "remove", "3.9", "3.10") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -11,11 +11,11 @@ def test_not_installed(hatch, helpers): def test_basic(hatch, helpers, temp_dir_data): - install_dir = temp_dir_data / 'data' / 'pythons' - for name in ('3.9', '3.10'): + install_dir = temp_dir_data / "data" / "pythons" + for name in ("3.9", "3.10"): helpers.write_distribution(install_dir, name) - result = hatch('python', 'remove', '3.9') + result = hatch("python", "remove", "3.9") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -24,15 +24,15 @@ def test_basic(hatch, helpers, temp_dir_data): """ ) - assert not (install_dir / '3.9').exists() - assert (install_dir / '3.10').is_dir() + assert not (install_dir / "3.9").exists() + assert (install_dir / "3.10").is_dir() def test_specific_location(hatch, helpers, temp_dir_data, dist_name): - install_dir = temp_dir_data / 'foo' / 'bar' / 'baz' + install_dir = temp_dir_data / "foo" / "bar" / "baz" helpers.write_distribution(install_dir, dist_name) - result = hatch('python', 'remove', '-d', str(install_dir), dist_name) + result = hatch("python", "remove", "-d", str(install_dir), dist_name) assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -45,16 +45,16 @@ def test_specific_location(hatch, helpers, temp_dir_data, dist_name): def test_all(hatch, helpers, temp_dir_data): - installed_distributions = ('3.9', '3.10', '3.11') + installed_distributions = ("3.9", "3.10", "3.11") for name in installed_distributions: - install_dir = temp_dir_data / 'data' / 'pythons' + install_dir = temp_dir_data / "data" / "pythons" helpers.write_distribution(install_dir, name) - result = hatch('python', 'remove', 'all') + result = hatch("python", "remove", "all") assert result.exit_code == 0, result.output - expected_lines = [f'Removing {name}' for name in installed_distributions] - expected_lines.append('') + expected_lines = [f"Removing {name}" for name in installed_distributions] + expected_lines.append("") - assert result.output == '\n'.join(expected_lines) + assert result.output == "\n".join(expected_lines) diff --git a/tests/cli/python/test_show.py b/tests/cli/python/test_show.py index 2f6389be4..9aec6d0e7 100644 --- a/tests/cli/python/test_show.py +++ b/tests/cli/python/test_show.py @@ -7,10 +7,10 @@ def render_table(title, rows): console = Console(force_terminal=False, no_color=True, legacy_windows=False) - table = Table(title=title, show_lines=True, title_style='', box=ASCII_DOUBLE_HEAD, safe_box=True) + table = Table(title=title, show_lines=True, title_style="", box=ASCII_DOUBLE_HEAD, safe_box=True) for column in rows[0]: - table.add_column(column, style='bold') + table.add_column(column, style="bold") for row in rows[1:]: table.add_row(*row) @@ -24,116 +24,116 @@ def render_table(title, rows): def test_nothing_installed(hatch): compatible_distributions = get_compatible_distributions() available_table = render_table( - 'Available', + "Available", [ - ['Name', 'Version'], + ["Name", "Version"], *[[d.name, d.version.base_version] for d in compatible_distributions.values()], ], ) - result = hatch('python', 'show', '--ascii') + result = hatch("python", "show", "--ascii") assert result.exit_code == 0, result.output assert result.output == available_table def test_some_installed(hatch, helpers, temp_dir_data, dist_name): - install_dir = temp_dir_data / 'data' / 'pythons' + install_dir = temp_dir_data / "data" / "pythons" helpers.write_distribution(install_dir, dist_name) compatible_distributions = get_compatible_distributions() installed_distribution = compatible_distributions.pop(dist_name) installed_table = render_table( - 'Installed', + "Installed", [ - ['Name', 'Version'], + ["Name", "Version"], [dist_name, installed_distribution.version.base_version], ], ) available_table = render_table( - 'Available', + "Available", [ - ['Name', 'Version'], + ["Name", "Version"], *[[d.name, d.version.base_version] for d in compatible_distributions.values()], ], ) - result = hatch('python', 'show', '--ascii') + result = hatch("python", "show", "--ascii") assert result.exit_code == 0, result.output assert result.output == installed_table + available_table def test_all_installed(hatch, helpers, temp_dir_data): - install_dir = temp_dir_data / 'data' / 'pythons' + install_dir = temp_dir_data / "data" / "pythons" compatible_distributions = get_compatible_distributions() for dist_name in compatible_distributions: helpers.write_distribution(install_dir, dist_name) installed_table = render_table( - 'Installed', + "Installed", [ - ['Name', 'Version'], + ["Name", "Version"], *[[d.name, d.version.base_version] for d in compatible_distributions.values()], ], ) - result = hatch('python', 'show', '--ascii') + result = hatch("python", "show", "--ascii") assert result.exit_code == 0, result.output assert result.output == installed_table def test_specific_location(hatch, helpers, temp_dir_data, dist_name): - install_dir = temp_dir_data / 'foo' / 'bar' / 'baz' + install_dir = temp_dir_data / "foo" / "bar" / "baz" helpers.write_distribution(install_dir, dist_name) compatible_distributions = get_compatible_distributions() installed_distribution = compatible_distributions.pop(dist_name) installed_table = render_table( - 'Installed', + "Installed", [ - ['Name', 'Version'], + ["Name", "Version"], [dist_name, installed_distribution.version.base_version], ], ) available_table = render_table( - 'Available', + "Available", [ - ['Name', 'Version'], + ["Name", "Version"], *[[d.name, d.version.base_version] for d in compatible_distributions.values()], ], ) - result = hatch('python', 'show', '--ascii', '-d', str(install_dir)) + result = hatch("python", "show", "--ascii", "-d", str(install_dir)) assert result.exit_code == 0, result.output assert result.output == installed_table + available_table def test_outdated(hatch, helpers, temp_dir_data, dist_name): - install_dir = temp_dir_data / 'data' / 'pythons' + install_dir = temp_dir_data / "data" / "pythons" helpers.write_distribution(install_dir, dist_name) helpers.downgrade_distribution_metadata(install_dir / dist_name) compatible_distributions = get_compatible_distributions() installed_distribution = compatible_distributions.pop(dist_name) installed_table = render_table( - 'Installed', + "Installed", [ - ['Name', 'Version', 'Status'], - [dist_name, helpers.downgrade_version(installed_distribution.version.base_version), 'Update available'], + ["Name", "Version", "Status"], + [dist_name, helpers.downgrade_version(installed_distribution.version.base_version), "Update available"], ], ) available_table = render_table( - 'Available', + "Available", [ - ['Name', 'Version'], + ["Name", "Version"], *[[d.name, d.version.base_version] for d in compatible_distributions.values()], ], ) - result = hatch('python', 'show', '--ascii') + result = hatch("python", "show", "--ascii") assert result.exit_code == 0, result.output assert result.output == installed_table + available_table diff --git a/tests/cli/python/test_update.py b/tests/cli/python/test_update.py index 15fa7337b..7681d1086 100644 --- a/tests/cli/python/test_update.py +++ b/tests/cli/python/test_update.py @@ -1,5 +1,5 @@ def test_not_installed(hatch, helpers): - result = hatch('python', 'update', '3.9', '3.10') + result = hatch("python", "update", "3.9", "3.10") assert result.exit_code == 1, result.output assert result.output == helpers.dedent( @@ -10,17 +10,17 @@ def test_not_installed(hatch, helpers): def test_basic(hatch, helpers, temp_dir_data, path_append, dist_name, mocker): - install_dir = temp_dir_data / 'data' / 'pythons' + install_dir = temp_dir_data / "data" / "pythons" helpers.write_distribution(install_dir, dist_name) dist_dir = install_dir / dist_name metadata = helpers.downgrade_distribution_metadata(dist_dir) - python_path = dist_dir / metadata['python_path'] + python_path = dist_dir / metadata["python_path"] install = mocker.patch( - 'hatch.python.core.PythonManager.install', return_value=mocker.MagicMock(path=dist_dir, python_path=python_path) + "hatch.python.core.PythonManager.install", return_value=mocker.MagicMock(path=dist_dir, python_path=python_path) ) - result = hatch('python', 'update', dist_name) + result = hatch("python", "update", dist_name) assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -35,18 +35,18 @@ def test_basic(hatch, helpers, temp_dir_data, path_append, dist_name, mocker): def test_specific_location(hatch, helpers, temp_dir_data, path_append, dist_name, mocker): - install = mocker.patch('hatch.python.core.PythonManager.install') - install_dir = temp_dir_data / 'foo' / 'bar' / 'baz' + install = mocker.patch("hatch.python.core.PythonManager.install") + install_dir = temp_dir_data / "foo" / "bar" / "baz" helpers.write_distribution(install_dir, dist_name) dist_dir = install_dir / dist_name metadata = helpers.downgrade_distribution_metadata(dist_dir) - python_path = dist_dir / metadata['python_path'] + python_path = dist_dir / metadata["python_path"] install = mocker.patch( - 'hatch.python.core.PythonManager.install', return_value=mocker.MagicMock(path=dist_dir, python_path=python_path) + "hatch.python.core.PythonManager.install", return_value=mocker.MagicMock(path=dist_dir, python_path=python_path) ) - result = hatch('python', 'update', '-d', str(install_dir), dist_name) + result = hatch("python", "update", "-d", str(install_dir), dist_name) assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -61,30 +61,30 @@ def test_specific_location(hatch, helpers, temp_dir_data, path_append, dist_name def test_all(hatch, helpers, temp_dir_data, path_append, mocker): - installed_distributions = ('3.9', '3.10', '3.11') + installed_distributions = ("3.9", "3.10", "3.11") mocked_dists = [] for name in installed_distributions: - install_dir = temp_dir_data / 'data' / 'pythons' + install_dir = temp_dir_data / "data" / "pythons" helpers.write_distribution(install_dir, name) dist_dir = install_dir / name metadata = helpers.downgrade_distribution_metadata(dist_dir) - python_path = dist_dir / metadata['python_path'] + python_path = dist_dir / metadata["python_path"] mocked_dists.append(mocker.MagicMock(path=dist_dir, python_path=python_path)) - install = mocker.patch('hatch.python.core.PythonManager.install', side_effect=mocked_dists) + install = mocker.patch("hatch.python.core.PythonManager.install", side_effect=mocked_dists) - result = hatch('python', 'update', 'all') + result = hatch("python", "update", "all") assert result.exit_code == 0, result.output expected_lines = [] for dist in mocked_dists: - expected_lines.extend((f'Updating {dist.path.name}', f'Updated {dist.path.name} @ {dist.path}')) - expected_lines.append('') + expected_lines.extend((f"Updating {dist.path.name}", f"Updated {dist.path.name} @ {dist.path}")) + expected_lines.append("") - assert result.output == '\n'.join(expected_lines) + assert result.output == "\n".join(expected_lines) assert install.call_args_list == [mocker.call(name) for name in installed_distributions] path_append.assert_not_called() diff --git a/tests/cli/run/test_run.py b/tests/cli/run/test_run.py index 16cccf629..b86bef2a5 100644 --- a/tests/cli/run/test_run.py +++ b/tests/cli/run/test_run.py @@ -12,47 +12,47 @@ from hatchling.utils.constants import DEFAULT_BUILD_SCRIPT, DEFAULT_CONFIG_FILE -@pytest.fixture(scope='module') +@pytest.fixture(scope="module") def available_python_version(): compatible_distributions = get_compatible_distributions() - current_version = f'{sys.version_info.major}.{sys.version_info.minor}' + current_version = f"{sys.version_info.major}.{sys.version_info.minor}" if current_version in compatible_distributions: return current_version - versions = [d for d in get_compatible_distributions() if not d.startswith('pypy')] + versions = [d for d in get_compatible_distributions() if not d.startswith("pypy")] return versions[-1] def test_help(hatch): - short = hatch('run', '-h') + short = hatch("run", "-h") assert short.exit_code == 0, short.output - long = hatch('run', '--help') + long = hatch("run", "--help") assert long.exit_code == 0, long.output assert short.output == long.output def test_automatic_creation(hatch, helpers, temp_dir, config_file): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() project = Project(project_path) - helpers.update_project_environment(project, 'default', {'skip-install': True, **project.config.envs['default']}) + helpers.update_project_environment(project, "default", {"skip-install": True, **project.config.envs["default"]}) with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('run', 'python', '-c', "import pathlib,sys;pathlib.Path('test.txt').write_text(sys.executable)") + result = hatch("run", "python", "-c", "import pathlib,sys;pathlib.Path('test.txt').write_text(sys.executable)") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -61,10 +61,10 @@ def test_automatic_creation(hatch, helpers, temp_dir, config_file): Checking dependencies """ ) - output_file = project_path / 'test.txt' + output_file = project_path / "test.txt" assert output_file.is_file() - env_data_path = data_path / 'env' / 'virtual' + env_data_path = data_path / "env" / "virtual" assert env_data_path.is_dir() project_data_path = env_data_path / project_path.name @@ -87,25 +87,25 @@ def test_automatic_creation(hatch, helpers, temp_dir, config_file): def test_no_compatibility_check_if_exists(hatch, helpers, temp_dir, config_file, mocker): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() project = Project(project_path) - helpers.update_project_environment(project, 'default', {'skip-install': True, **project.config.envs['default']}) + helpers.update_project_environment(project, "default", {"skip-install": True, **project.config.envs["default"]}) with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('run', 'python', '-c', "import pathlib,sys;pathlib.Path('test.txt').write_text(sys.executable)") + result = hatch("run", "python", "-c", "import pathlib,sys;pathlib.Path('test.txt').write_text(sys.executable)") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -114,10 +114,10 @@ def test_no_compatibility_check_if_exists(hatch, helpers, temp_dir, config_file, Checking dependencies """ ) - output_file = project_path / 'test.txt' + output_file = project_path / "test.txt" assert output_file.is_file() - env_data_path = data_path / 'env' / 'virtual' + env_data_path = data_path / "env" / "virtual" assert env_data_path.is_dir() project_data_path = env_data_path / project_path.name @@ -139,9 +139,9 @@ def test_no_compatibility_check_if_exists(hatch, helpers, temp_dir, config_file, assert str(env_path) in str(output_file.read_text()) output_file.unlink() - mocker.patch('hatch.env.virtual.VirtualEnvironment.check_compatibility', side_effect=Exception('incompatible')) + mocker.patch("hatch.env.virtual.VirtualEnvironment.check_compatibility", side_effect=Exception("incompatible")) with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('run', 'python', '-c', "import pathlib,sys;pathlib.Path('test.txt').write_text(sys.executable)") + result = hatch("run", "python", "-c", "import pathlib,sys;pathlib.Path('test.txt').write_text(sys.executable)") assert result.exit_code == 0, result.output assert not result.output @@ -149,31 +149,31 @@ def test_no_compatibility_check_if_exists(hatch, helpers, temp_dir, config_file, def test_enter_project_directory(hatch, config_file, helpers, temp_dir): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() - project = 'foo' - config_file.model.mode = 'project' + project = "foo" + config_file.model.mode = "project" config_file.model.project = project config_file.model.projects = {project: str(project_path)} config_file.save() project = Project(project_path) - helpers.update_project_environment(project, 'default', {'skip-install': True, **project.config.envs['default']}) + helpers.update_project_environment(project, "default", {"skip-install": True, **project.config.envs["default"]}) with EnvVars({ConfigEnvVars.DATA: str(data_path)}): - result = hatch('run', 'python', '-c', "import pathlib,sys;pathlib.Path('test.txt').write_text(sys.executable)") + result = hatch("run", "python", "-c", "import pathlib,sys;pathlib.Path('test.txt').write_text(sys.executable)") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -182,10 +182,10 @@ def test_enter_project_directory(hatch, config_file, helpers, temp_dir): Checking dependencies """ ) - output_file = project_path / 'test.txt' + output_file = project_path / "test.txt" assert output_file.is_file() - env_data_path = data_path / 'env' / 'virtual' + env_data_path = data_path / "env" / "virtual" assert env_data_path.is_dir() project_data_path = env_data_path / project_path.name @@ -209,25 +209,25 @@ def test_enter_project_directory(hatch, config_file, helpers, temp_dir): @pytest.mark.requires_internet def test_sync_dependencies(hatch, helpers, temp_dir, config_file): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() project = Project(project_path) - helpers.update_project_environment(project, 'default', {'skip-install': True, **project.config.envs['default']}) + helpers.update_project_environment(project, "default", {"skip-install": True, **project.config.envs["default"]}) with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('env', 'create', 'default') + result = hatch("env", "create", "default") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -237,7 +237,7 @@ def test_sync_dependencies(hatch, helpers, temp_dir, config_file): """ ) - env_data_path = data_path / 'env' / 'virtual' + env_data_path = data_path / "env" / "virtual" assert env_data_path.is_dir() project_data_path = env_data_path / project_path.name @@ -258,14 +258,14 @@ def test_sync_dependencies(hatch, helpers, temp_dir, config_file): project = Project(project_path) helpers.update_project_environment( - project, 'default', {'dependencies': ['binary'], **project.config.envs['default']} + project, "default", {"dependencies": ["binary"], **project.config.envs["default"]} ) with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): result = hatch( - 'run', - 'python', - '-c', + "run", + "python", + "-c", "import binary,pathlib,sys;pathlib.Path('test.txt').write_text(str(binary.convert_units(1024)))", ) @@ -276,7 +276,7 @@ def test_sync_dependencies(hatch, helpers, temp_dir, config_file): Syncing dependencies """ ) - output_file = project_path / 'test.txt' + output_file = project_path / "test.txt" assert output_file.is_file() assert str(output_file.read_text()) == "(1.0, 'KiB')" @@ -284,22 +284,22 @@ def test_sync_dependencies(hatch, helpers, temp_dir, config_file): @pytest.mark.requires_internet def test_sync_project_dependencies(hatch, helpers, temp_dir, config_file): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('env', 'create', 'default') + result = hatch("env", "create", "default") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -310,7 +310,7 @@ def test_sync_project_dependencies(hatch, helpers, temp_dir, config_file): """ ) - env_data_path = data_path / 'env' / 'virtual' + env_data_path = data_path / "env" / "virtual" assert env_data_path.is_dir() project_data_path = env_data_path / project_path.name @@ -331,14 +331,14 @@ def test_sync_project_dependencies(hatch, helpers, temp_dir, config_file): project = Project(project_path) config = dict(project.raw_config) - config['project']['dependencies'] = ['binary'] + config["project"]["dependencies"] = ["binary"] project.save_config(config) with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): result = hatch( - 'run', - 'python', - '-c', + "run", + "python", + "-c", "import binary,pathlib,sys;pathlib.Path('test.txt').write_text(str(binary.convert_units(1024)))", ) @@ -349,7 +349,7 @@ def test_sync_project_dependencies(hatch, helpers, temp_dir, config_file): Syncing dependencies """ ) - output_file = project_path / 'test.txt' + output_file = project_path / "test.txt" assert output_file.is_file() assert str(output_file.read_text()) == "(1.0, 'KiB')" @@ -357,28 +357,28 @@ def test_sync_project_dependencies(hatch, helpers, temp_dir, config_file): @pytest.mark.requires_internet def test_sync_project_features(hatch, helpers, temp_dir, config_file): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() project = Project(project_path) config = dict(project.raw_config) - config['project']['optional-dependencies'] = {'foo': []} + config["project"]["optional-dependencies"] = {"foo": []} project.save_config(config) - helpers.update_project_environment(project, 'default', {'features': ['foo'], **project.config.envs['default']}) + helpers.update_project_environment(project, "default", {"features": ["foo"], **project.config.envs["default"]}) with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('env', 'create', 'default') + result = hatch("env", "create", "default") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -389,7 +389,7 @@ def test_sync_project_features(hatch, helpers, temp_dir, config_file): """ ) - env_data_path = data_path / 'env' / 'virtual' + env_data_path = data_path / "env" / "virtual" assert env_data_path.is_dir() project_data_path = env_data_path / project_path.name @@ -410,14 +410,14 @@ def test_sync_project_features(hatch, helpers, temp_dir, config_file): project = Project(project_path) config = dict(project.raw_config) - config['project']['optional-dependencies']['foo'].append('binary') + config["project"]["optional-dependencies"]["foo"].append("binary") project.save_config(config) with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): result = hatch( - 'run', - 'python', - '-c', + "run", + "python", + "-c", "import binary,pathlib,sys;pathlib.Path('test.txt').write_text(str(binary.convert_units(1024)))", ) @@ -428,7 +428,7 @@ def test_sync_project_features(hatch, helpers, temp_dir, config_file): Syncing dependencies """ ) - output_file = project_path / 'test.txt' + output_file = project_path / "test.txt" assert output_file.is_file() assert str(output_file.read_text()) == "(1.0, 'KiB')" @@ -436,25 +436,25 @@ def test_sync_project_features(hatch, helpers, temp_dir, config_file): @pytest.mark.requires_internet def test_dependency_hash_checking(hatch, helpers, temp_dir, config_file, mocker): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() project = Project(project_path) - helpers.update_project_environment(project, 'default', {'skip-install': True, **project.config.envs['default']}) + helpers.update_project_environment(project, "default", {"skip-install": True, **project.config.envs["default"]}) with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('env', 'create', 'default') + result = hatch("env", "create", "default") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -464,7 +464,7 @@ def test_dependency_hash_checking(hatch, helpers, temp_dir, config_file, mocker) """ ) - env_data_path = data_path / 'env' / 'virtual' + env_data_path = data_path / "env" / "virtual" assert env_data_path.is_dir() project_data_path = env_data_path / project_path.name @@ -485,14 +485,14 @@ def test_dependency_hash_checking(hatch, helpers, temp_dir, config_file, mocker) project = Project(project_path) helpers.update_project_environment( - project, 'default', {'dependencies': ['binary'], **project.config.envs['default']} + project, "default", {"dependencies": ["binary"], **project.config.envs["default"]} ) with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): result = hatch( - 'run', - 'python', - '-c', + "run", + "python", + "-c", "import binary,pathlib,sys;pathlib.Path('test.txt').write_text(str(binary.convert_units(1024)))", ) @@ -503,7 +503,7 @@ def test_dependency_hash_checking(hatch, helpers, temp_dir, config_file, mocker) Syncing dependencies """ ) - output_file = project_path / 'test.txt' + output_file = project_path / "test.txt" assert output_file.is_file() assert str(output_file.read_text()) == "(1.0, 'KiB')" @@ -512,30 +512,30 @@ def test_dependency_hash_checking(hatch, helpers, temp_dir, config_file, mocker) # Now there should be no output because there is no dependency checking with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): result = hatch( - 'run', - 'python', - '-c', + "run", + "python", + "-c", "import binary,pathlib,sys;pathlib.Path('test.txt').write_text(str(binary.convert_units(1024)))", ) assert result.exit_code == 0, result.output assert not result.output - output_file = project_path / 'test.txt' + output_file = project_path / "test.txt" assert output_file.is_file() assert str(output_file.read_text()) == "(1.0, 'KiB')" output_file.unlink() - mocker.patch('hatch.env.virtual.VirtualEnvironment.dependencies_in_sync', return_value=False) - mocker.patch('hatch.env.virtual.VirtualEnvironment.dependency_hash', side_effect=['foo', 'bar', 'bar']) + mocker.patch("hatch.env.virtual.VirtualEnvironment.dependencies_in_sync", return_value=False) + mocker.patch("hatch.env.virtual.VirtualEnvironment.dependency_hash", side_effect=["foo", "bar", "bar"]) # Ensure that the saved value is the second value with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): result = hatch( - 'run', - 'python', - '-c', + "run", + "python", + "-c", "import binary,pathlib,sys;pathlib.Path('test.txt').write_text(str(binary.convert_units(1024)))", ) @@ -547,7 +547,7 @@ def test_dependency_hash_checking(hatch, helpers, temp_dir, config_file, mocker) """ ) - output_file = project_path / 'test.txt' + output_file = project_path / "test.txt" assert output_file.is_file() assert str(output_file.read_text()) == "(1.0, 'KiB')" @@ -555,45 +555,45 @@ def test_dependency_hash_checking(hatch, helpers, temp_dir, config_file, mocker) with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): result = hatch( - 'run', - 'python', - '-c', + "run", + "python", + "-c", "import binary,pathlib,sys;pathlib.Path('test.txt').write_text(str(binary.convert_units(1024)))", ) assert result.exit_code == 0, result.output assert not result.output - output_file = project_path / 'test.txt' + output_file = project_path / "test.txt" assert output_file.is_file() assert str(output_file.read_text()) == "(1.0, 'KiB')" def test_scripts(hatch, helpers, temp_dir, config_file): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() project = Project(project_path) helpers.update_project_environment( project, - 'default', - {'skip-install': True, 'scripts': {'py': 'python -c {args}'}, **project.config.envs['default']}, + "default", + {"skip-install": True, "scripts": {"py": "python -c {args}"}, **project.config.envs["default"]}, ) with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('run', 'py', "import pathlib,sys;pathlib.Path('test.txt').write_text(sys.executable)") + result = hatch("run", "py", "import pathlib,sys;pathlib.Path('test.txt').write_text(sys.executable)") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -602,10 +602,10 @@ def test_scripts(hatch, helpers, temp_dir, config_file): Checking dependencies """ ) - output_file = project_path / 'test.txt' + output_file = project_path / "test.txt" assert output_file.is_file() - env_data_path = data_path / 'env' / 'virtual' + env_data_path = data_path / "env" / "virtual" assert env_data_path.is_dir() project_data_path = env_data_path / project_path.name @@ -628,32 +628,32 @@ def test_scripts(hatch, helpers, temp_dir, config_file): def test_scripts_specific_environment(hatch, helpers, temp_dir, config_file): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() project = Project(project_path) helpers.update_project_environment( project, - 'default', - {'skip-install': True, 'scripts': {'py': 'python -c {args}'}, **project.config.envs['default']}, + "default", + {"skip-install": True, "scripts": {"py": "python -c {args}"}, **project.config.envs["default"]}, ) - helpers.update_project_environment(project, 'test', {'env-vars': {'foo': 'bar'}}) + helpers.update_project_environment(project, "test", {"env-vars": {"foo": "bar"}}) with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): result = hatch( - 'run', - 'test:py', + "run", + "test:py", "import os,pathlib,sys;pathlib.Path('test.txt').write_text(" "sys.executable+os.linesep[-1]+os.environ['foo'])", ) @@ -665,10 +665,10 @@ def test_scripts_specific_environment(hatch, helpers, temp_dir, config_file): Checking dependencies """ ) - output_file = project_path / 'test.txt' + output_file = project_path / "test.txt" assert output_file.is_file() - env_data_path = data_path / 'env' / 'virtual' + env_data_path = data_path / "env" / "virtual" assert env_data_path.is_dir() project_data_path = env_data_path / project_path.name @@ -685,36 +685,36 @@ def test_scripts_specific_environment(hatch, helpers, temp_dir, config_file): env_path = env_dirs[0] - assert env_path.name == 'test' + assert env_path.name == "test" python_executable_path, env_var_value = str(output_file.read_text()).splitlines() assert str(env_path) in python_executable_path - assert env_var_value == 'bar' + assert env_var_value == "bar" @pytest.mark.requires_internet def test_scripts_no_environment(hatch, helpers, temp_dir, config_file): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() project = Project(project_path) config = dict(project.raw_config) - config['tool']['hatch']['scripts'] = {'py': 'python -c {args}'} + config["tool"]["hatch"]["scripts"] = {"py": "python -c {args}"} project.save_config(config) with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('run', ':py', "import pathlib,sys;pathlib.Path('test.txt').write_text(sys.executable)") + result = hatch("run", ":py", "import pathlib,sys;pathlib.Path('test.txt').write_text(sys.executable)") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -723,48 +723,48 @@ def test_scripts_no_environment(hatch, helpers, temp_dir, config_file): """ ) - output_file = project_path / 'test.txt' + output_file = project_path / "test.txt" assert output_file.is_file() - env_data_path = data_path / 'env' / 'virtual' + env_data_path = data_path / "env" / "virtual" assert not env_data_path.exists() assert os.path.realpath(output_file.read_text().strip()).lower() == os.path.realpath(sys.executable).lower() def test_error(hatch, helpers, temp_dir, config_file): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() project = Project(project_path) helpers.update_project_environment( project, - 'default', + "default", { - 'skip-install': True, - 'scripts': { - 'error': [ + "skip-install": True, + "scripts": { + "error": [ 'python -c "import sys;sys.exit(3)"', - 'python -c "import pathlib,sys;pathlib.Path(\'test.txt\').write_text(sys.executable)"', + "python -c \"import pathlib,sys;pathlib.Path('test.txt').write_text(sys.executable)\"", ], }, - **project.config.envs['default'], + **project.config.envs["default"], }, ) with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('run', 'error') + result = hatch("run", "error") assert result.exit_code == 3 assert result.output == helpers.dedent( @@ -774,43 +774,43 @@ def test_error(hatch, helpers, temp_dir, config_file): cmd [1] | python -c "import sys;sys.exit(3)" """ ) - output_file = project_path / 'test.txt' + output_file = project_path / "test.txt" assert not output_file.is_file() def test_ignore_error(hatch, helpers, temp_dir, config_file): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() project = Project(project_path) helpers.update_project_environment( project, - 'default', + "default", { - 'skip-install': True, - 'scripts': { - 'error': [ + "skip-install": True, + "scripts": { + "error": [ '- python -c "import sys;sys.exit(3)"', - 'python -c "import pathlib,sys;pathlib.Path(\'test.txt\').write_text(sys.executable)"', + "python -c \"import pathlib,sys;pathlib.Path('test.txt').write_text(sys.executable)\"", ], }, - **project.config.envs['default'], + **project.config.envs["default"], }, ) with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('run', 'error') + result = hatch("run", "error") assert result.exit_code == 0 assert result.output == helpers.dedent( @@ -821,10 +821,10 @@ def test_ignore_error(hatch, helpers, temp_dir, config_file): cmd [2] | python -c "import pathlib,sys;pathlib.Path('test.txt').write_text(sys.executable)" """ ) - output_file = project_path / 'test.txt' + output_file = project_path / "test.txt" assert output_file.is_file() - env_data_path = data_path / 'env' / 'virtual' + env_data_path = data_path / "env" / "virtual" assert env_data_path.is_dir() project_data_path = env_data_path / project_path.name @@ -847,29 +847,29 @@ def test_ignore_error(hatch, helpers, temp_dir, config_file): def test_command_expansion_error(hatch, helpers, temp_dir, config_file): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() project = Project(project_path) helpers.update_project_environment( project, - 'default', - {'skip-install': True, 'scripts': {'error': ['echo {env:FOOBAR}']}, **project.config.envs['default']}, + "default", + {"skip-install": True, "scripts": {"error": ["echo {env:FOOBAR}"]}, **project.config.envs["default"]}, ) with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('run', 'error') + result = hatch("run", "error") assert result.exit_code == 1 assert result.output == helpers.dedent( @@ -879,44 +879,44 @@ def test_command_expansion_error(hatch, helpers, temp_dir, config_file): Nonexistent environment variable must set a default: FOOBAR """ ) - output_file = project_path / 'test.txt' + output_file = project_path / "test.txt" assert not output_file.is_file() def test_verbosity(hatch, helpers, temp_dir, config_file): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() project = Project(project_path) helpers.update_project_environment( project, - 'default', + "default", { - 'skip-install': True, - 'scripts': { - 'write-exe': [ - 'python -c "import pathlib,sys;pathlib.Path(\'{args}1.txt\').write_text(sys.executable)"', - 'python -c "import pathlib,sys;pathlib.Path(\'{args}2.txt\').write_text(sys.executable)"', - 'python -c "import pathlib,sys;pathlib.Path(\'{args}3.txt\').write_text(sys.executable)"', + "skip-install": True, + "scripts": { + "write-exe": [ + "python -c \"import pathlib,sys;pathlib.Path('{args}1.txt').write_text(sys.executable)\"", + "python -c \"import pathlib,sys;pathlib.Path('{args}2.txt').write_text(sys.executable)\"", + "python -c \"import pathlib,sys;pathlib.Path('{args}3.txt').write_text(sys.executable)\"", ], }, - **project.config.envs['default'], + **project.config.envs["default"], }, ) with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('-v', 'run', 'write-exe', 'test') + result = hatch("-v", "run", "write-exe", "test") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -931,11 +931,11 @@ def test_verbosity(hatch, helpers, temp_dir, config_file): ) output_files = [] for i in range(1, 4): - output_file = project_path / f'test{i}.txt' + output_file = project_path / f"test{i}.txt" assert output_file.is_file() output_files.append(output_file) - env_data_path = data_path / 'env' / 'virtual' + env_data_path = data_path / "env" / "virtual" assert env_data_path.is_dir() project_data_path = env_data_path / project_path.name @@ -959,27 +959,27 @@ def test_verbosity(hatch, helpers, temp_dir, config_file): def test_matrix_no_environments(hatch, helpers, temp_dir, config_file): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() project = Project(project_path) - helpers.update_project_environment(project, 'default', {'skip-install': True, **project.config.envs['default']}) - helpers.update_project_environment(project, 'test', {'matrix': []}) + helpers.update_project_environment(project, "default", {"skip-install": True, **project.config.envs["default"]}) + helpers.update_project_environment(project, "test", {"matrix": []}) with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): result = hatch( - 'run', 'test:python', '-c', "import os,sys;open('test.txt', 'a').write(sys.executable+os.linesep[-1])" + "run", "test:python", "-c", "import os,sys;open('test.txt', 'a').write(sys.executable+os.linesep[-1])" ) assert result.exit_code == 1, result.output @@ -991,27 +991,27 @@ def test_matrix_no_environments(hatch, helpers, temp_dir, config_file): def test_matrix(hatch, helpers, temp_dir, config_file): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() project = Project(project_path) - helpers.update_project_environment(project, 'default', {'skip-install': True, **project.config.envs['default']}) - helpers.update_project_environment(project, 'test', {'matrix': [{'version': ['9000', '42']}]}) + helpers.update_project_environment(project, "default", {"skip-install": True, **project.config.envs["default"]}) + helpers.update_project_environment(project, "test", {"matrix": [{"version": ["9000", "42"]}]}) with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): result = hatch( - 'run', 'test:python', '-c', "import os,sys;open('test.txt', 'a').write(sys.executable+os.linesep[-1])" + "run", "test:python", "-c", "import os,sys;open('test.txt', 'a').write(sys.executable+os.linesep[-1])" ) assert result.exit_code == 0, result.output @@ -1025,10 +1025,10 @@ def test_matrix(hatch, helpers, temp_dir, config_file): Checking dependencies """ ) - output_file = project_path / 'test.txt' + output_file = project_path / "test.txt" assert output_file.is_file() - env_data_path = data_path / 'env' / 'virtual' + env_data_path = data_path / "env" / "virtual" assert env_data_path.is_dir() project_data_path = env_data_path / project_path.name @@ -1044,35 +1044,35 @@ def test_matrix(hatch, helpers, temp_dir, config_file): assert len(env_dirs) == 2 python_path1, python_path2 = str(output_file.read_text()).splitlines() - for python_path, env_dir, env_name in zip((python_path1, python_path2), env_dirs, ('test.9000', 'test.42')): + for python_path, env_dir, env_name in zip((python_path1, python_path2), env_dirs, ("test.9000", "test.42")): assert env_dir.name == env_name assert str(env_dir) in python_path def test_incompatible_single(hatch, helpers, temp_dir, config_file): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() project = Project(project_path) helpers.update_project_environment( - project, 'default', {'skip-install': True, 'platforms': ['foo'], **project.config.envs['default']} + project, "default", {"skip-install": True, "platforms": ["foo"], **project.config.envs["default"]} ) - helpers.update_project_environment(project, 'test', {}) + helpers.update_project_environment(project, "test", {}) with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): result = hatch( - 'run', 'test:python', '-c', "import os,sys;open('test.txt', 'a').write(sys.executable+os.linesep[-1])" + "run", "test:python", "-c", "import os,sys;open('test.txt', 'a').write(sys.executable+os.linesep[-1])" ) assert result.exit_code == 1 @@ -1081,37 +1081,37 @@ def test_incompatible_single(hatch, helpers, temp_dir, config_file): Environment `test` is incompatible: unsupported platform """ ) - output_file = project_path / 'test.txt' + output_file = project_path / "test.txt" assert not output_file.is_file() - env_data_path = data_path / 'env' / 'virtual' + env_data_path = data_path / "env" / "virtual" assert not env_data_path.is_dir() def test_incompatible_matrix_full(hatch, helpers, temp_dir, config_file): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() project = Project(project_path) helpers.update_project_environment( - project, 'default', {'skip-install': True, 'platforms': ['foo'], **project.config.envs['default']} + project, "default", {"skip-install": True, "platforms": ["foo"], **project.config.envs["default"]} ) - helpers.update_project_environment(project, 'test', {'matrix': [{'version': ['9000', '42']}]}) + helpers.update_project_environment(project, "test", {"matrix": [{"version": ["9000", "42"]}]}) with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): result = hatch( - 'run', 'test:python', '-c', "import os,sys;open('test.txt', 'a').write(sys.executable+os.linesep[-1])" + "run", "test:python", "-c", "import os,sys;open('test.txt', 'a').write(sys.executable+os.linesep[-1])" ) assert result.exit_code == 0, result.output @@ -1122,42 +1122,42 @@ def test_incompatible_matrix_full(hatch, helpers, temp_dir, config_file): test.42 -> unsupported platform """ ) - output_file = project_path / 'test.txt' + output_file = project_path / "test.txt" assert not output_file.is_file() - env_data_path = data_path / 'env' / 'virtual' + env_data_path = data_path / "env" / "virtual" assert not env_data_path.is_dir() def test_incompatible_matrix_partial(hatch, helpers, temp_dir, config_file): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() project = Project(project_path) - helpers.update_project_environment(project, 'default', {'skip-install': True, **project.config.envs['default']}) + helpers.update_project_environment(project, "default", {"skip-install": True, **project.config.envs["default"]}) helpers.update_project_environment( project, - 'test', + "test", { - 'matrix': [{'version': ['9000', '42']}], - 'overrides': {'matrix': {'version': {'platforms': [{'value': 'foo', 'if': ['9000']}]}}}, + "matrix": [{"version": ["9000", "42"]}], + "overrides": {"matrix": {"version": {"platforms": [{"value": "foo", "if": ["9000"]}]}}}, }, ) with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): result = hatch( - 'run', 'test:python', '-c', "import os,sys;open('test.txt', 'a').write(sys.executable+os.linesep[-1])" + "run", "test:python", "-c", "import os,sys;open('test.txt', 'a').write(sys.executable+os.linesep[-1])" ) assert result.exit_code == 0, result.output @@ -1171,10 +1171,10 @@ def test_incompatible_matrix_partial(hatch, helpers, temp_dir, config_file): test.9000 -> unsupported platform """ ) - output_file = project_path / 'test.txt' + output_file = project_path / "test.txt" assert output_file.is_file() - env_data_path = data_path / 'env' / 'virtual' + env_data_path = data_path / "env" / "virtual" assert env_data_path.is_dir() project_data_path = env_data_path / project_path.name @@ -1190,40 +1190,40 @@ def test_incompatible_matrix_partial(hatch, helpers, temp_dir, config_file): assert len(env_dirs) == 1 env_path = env_dirs[0] - assert env_path.name == 'test.42' + assert env_path.name == "test.42" python_path = str(output_file.read_text()).strip() assert str(env_path) in python_path def test_incompatible_missing_python(hatch, helpers, temp_dir, config_file): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() - known_version = ''.join(map(str, sys.version_info[:2])) + known_version = "".join(map(str, sys.version_info[:2])) project = Project(project_path) - helpers.update_project_environment(project, 'default', {'skip-install': True, **project.config.envs['default']}) - helpers.update_project_environment(project, 'test', {'matrix': [{'python': [known_version, '9000']}]}) + helpers.update_project_environment(project, "default", {"skip-install": True, **project.config.envs["default"]}) + helpers.update_project_environment(project, "test", {"matrix": [{"python": [known_version, "9000"]}]}) with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): result = hatch( - 'run', 'test:python', '-c', "import os,sys;open('test.txt', 'a').write(sys.executable+os.linesep[-1])" + "run", "test:python", "-c", "import os,sys;open('test.txt', 'a').write(sys.executable+os.linesep[-1])" ) - padding = '─' + padding = "─" if len(known_version) < 3: - padding += '─' + padding += "─" assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -1236,10 +1236,10 @@ def test_incompatible_missing_python(hatch, helpers, temp_dir, config_file): test.py9000 -> cannot locate Python: 9000 """ ) - output_file = project_path / 'test.txt' + output_file = project_path / "test.txt" assert output_file.is_file() - env_data_path = data_path / 'env' / 'virtual' + env_data_path = data_path / "env" / "virtual" assert env_data_path.is_dir() project_data_path = env_data_path / project_path.name @@ -1255,33 +1255,33 @@ def test_incompatible_missing_python(hatch, helpers, temp_dir, config_file): assert len(env_dirs) == 1 env_path = env_dirs[0] - assert env_path.name == f'test.py{known_version}' + assert env_path.name == f"test.py{known_version}" python_path = str(output_file.read_text()).strip() assert str(env_path) in python_path def test_env_detection(hatch, helpers, temp_dir, config_file): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() project = Project(project_path) - helpers.update_project_environment(project, 'default', {'skip-install': True, **project.config.envs['default']}) - helpers.update_project_environment(project, 'foo', {}) + helpers.update_project_environment(project, "default", {"skip-install": True, **project.config.envs["default"]}) + helpers.update_project_environment(project, "foo", {}) with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('env', 'create') + result = hatch("env", "create") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -1292,7 +1292,7 @@ def test_env_detection(hatch, helpers, temp_dir, config_file): ) with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('env', 'create', 'foo') + result = hatch("env", "create", "foo") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -1302,7 +1302,7 @@ def test_env_detection(hatch, helpers, temp_dir, config_file): """ ) - env_data_path = data_path / 'env' / 'virtual' + env_data_path = data_path / "env" / "virtual" assert env_data_path.is_dir() project_data_path = env_data_path / project_path.name @@ -1317,15 +1317,15 @@ def test_env_detection(hatch, helpers, temp_dir, config_file): env_dirs = sorted(storage_path.iterdir(), key=lambda d: d.name) assert len(env_dirs) == 2 - assert env_dirs[0].name == 'foo' - assert env_dirs[1].name == 'my-app' + assert env_dirs[0].name == "foo" + assert env_dirs[1].name == "my-app" - with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path), AppEnvVars.ENV_ACTIVE: 'foo'}): - result = hatch('run', 'python', '-c', "import pathlib,sys;pathlib.Path('test.txt').write_text(sys.executable)") + with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path), AppEnvVars.ENV_ACTIVE: "foo"}): + result = hatch("run", "python", "-c", "import pathlib,sys;pathlib.Path('test.txt').write_text(sys.executable)") assert result.exit_code == 0, result.output - output_file = project_path / 'test.txt' + output_file = project_path / "test.txt" assert output_file.is_file() python_path = str(output_file.read_text()).strip() @@ -1333,26 +1333,26 @@ def test_env_detection(hatch, helpers, temp_dir, config_file): def test_env_detection_override(hatch, helpers, temp_dir, config_file): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() project = Project(project_path) - helpers.update_project_environment(project, 'default', {'skip-install': True, **project.config.envs['default']}) - helpers.update_project_environment(project, 'foo', {}) + helpers.update_project_environment(project, "default", {"skip-install": True, **project.config.envs["default"]}) + helpers.update_project_environment(project, "foo", {}) with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('env', 'create') + result = hatch("env", "create") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -1363,7 +1363,7 @@ def test_env_detection_override(hatch, helpers, temp_dir, config_file): ) with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('env', 'create', 'foo') + result = hatch("env", "create", "foo") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -1373,7 +1373,7 @@ def test_env_detection_override(hatch, helpers, temp_dir, config_file): """ ) - env_data_path = data_path / 'env' / 'virtual' + env_data_path = data_path / "env" / "virtual" assert env_data_path.is_dir() project_data_path = env_data_path / project_path.name @@ -1388,17 +1388,17 @@ def test_env_detection_override(hatch, helpers, temp_dir, config_file): env_dirs = sorted(storage_path.iterdir(), key=lambda d: d.name) assert len(env_dirs) == 2 - assert env_dirs[0].name == 'foo' - assert env_dirs[1].name == 'my-app' + assert env_dirs[0].name == "foo" + assert env_dirs[1].name == "my-app" - with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path), AppEnvVars.ENV_ACTIVE: 'foo'}): + with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path), AppEnvVars.ENV_ACTIVE: "foo"}): result = hatch( - 'run', 'default:python', '-c', "import pathlib,sys;pathlib.Path('test.txt').write_text(sys.executable)" + "run", "default:python", "-c", "import pathlib,sys;pathlib.Path('test.txt').write_text(sys.executable)" ) assert result.exit_code == 0, result.output - output_file = project_path / 'test.txt' + output_file = project_path / "test.txt" assert output_file.is_file() python_path = str(output_file.read_text()).strip() @@ -1406,26 +1406,26 @@ def test_env_detection_override(hatch, helpers, temp_dir, config_file): def test_matrix_variable_selection_no_command(hatch, helpers, temp_dir, config_file): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() project = Project(project_path) - helpers.update_project_environment(project, 'default', {'skip-install': True, **project.config.envs['default']}) - helpers.update_project_environment(project, 'test', {'matrix': [{'version': ['9000', '42']}]}) + helpers.update_project_environment(project, "default", {"skip-install": True, **project.config.envs["default"]}) + helpers.update_project_environment(project, "test", {"matrix": [{"version": ["9000", "42"]}]}) with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('run', '+version=9000') + result = hatch("run", "+version=9000") assert result.exit_code == 1, result.output assert result.output == helpers.dedent( @@ -1436,31 +1436,31 @@ def test_matrix_variable_selection_no_command(hatch, helpers, temp_dir, config_f def test_matrix_variable_selection_duplicate_inclusion(hatch, helpers, temp_dir, config_file): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() project = Project(project_path) - helpers.update_project_environment(project, 'default', {'skip-install': True, **project.config.envs['default']}) - helpers.update_project_environment(project, 'test', {'matrix': [{'version': ['9000', '42']}]}) + helpers.update_project_environment(project, "default", {"skip-install": True, **project.config.envs["default"]}) + helpers.update_project_environment(project, "test", {"matrix": [{"version": ["9000", "42"]}]}) with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): result = hatch( - 'run', - '+version=9000', - '+version=42', - 'python', - '-c', + "run", + "+version=9000", + "+version=42", + "python", + "-c", "import pathlib,sys;pathlib.Path('test.txt').write_text(sys.executable)", ) @@ -1473,31 +1473,31 @@ def test_matrix_variable_selection_duplicate_inclusion(hatch, helpers, temp_dir, def test_matrix_variable_selection_duplicate_exclusion(hatch, helpers, temp_dir, config_file): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() project = Project(project_path) - helpers.update_project_environment(project, 'default', {'skip-install': True, **project.config.envs['default']}) - helpers.update_project_environment(project, 'test', {'matrix': [{'version': ['9000', '42']}]}) + helpers.update_project_environment(project, "default", {"skip-install": True, **project.config.envs["default"]}) + helpers.update_project_environment(project, "test", {"matrix": [{"version": ["9000", "42"]}]}) with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): result = hatch( - 'run', - '-version=9000', - '-version=42', - 'python', - '-c', + "run", + "-version=9000", + "-version=42", + "python", + "-c", "import pathlib,sys;pathlib.Path('test.txt').write_text(sys.executable)", ) @@ -1510,31 +1510,31 @@ def test_matrix_variable_selection_duplicate_exclusion(hatch, helpers, temp_dir, def test_matrix_variable_selection_python_alias(hatch, helpers, temp_dir, config_file): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() project = Project(project_path) - helpers.update_project_environment(project, 'default', {'skip-install': True, **project.config.envs['default']}) - helpers.update_project_environment(project, 'test', {'matrix': [{'python': ['9000', '42']}]}) + helpers.update_project_environment(project, "default", {"skip-install": True, **project.config.envs["default"]}) + helpers.update_project_environment(project, "test", {"matrix": [{"python": ["9000", "42"]}]}) with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): result = hatch( - 'run', - '+py=9000', - '+python=42', - 'python', - '-c', + "run", + "+py=9000", + "+python=42", + "python", + "-c", "import pathlib,sys;pathlib.Path('test.txt').write_text(sys.executable)", ) @@ -1547,30 +1547,30 @@ def test_matrix_variable_selection_python_alias(hatch, helpers, temp_dir, config def test_matrix_variable_selection_not_matrix(hatch, helpers, temp_dir, config_file): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() project = Project(project_path) - helpers.update_project_environment(project, 'default', {'skip-install': True, **project.config.envs['default']}) - helpers.update_project_environment(project, 'test', {'matrix': [{'version': ['9000', '42']}]}) + helpers.update_project_environment(project, "default", {"skip-install": True, **project.config.envs["default"]}) + helpers.update_project_environment(project, "test", {"matrix": [{"version": ["9000", "42"]}]}) with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): result = hatch( - 'run', - '+version=9000', - 'python', - '-c', + "run", + "+version=9000", + "python", + "-c", "import os,sys;open('test.txt', 'a').write(sys.executable+os.linesep[-1])", ) @@ -1583,30 +1583,30 @@ def test_matrix_variable_selection_not_matrix(hatch, helpers, temp_dir, config_f def test_matrix_variable_selection_inclusion(hatch, helpers, temp_dir, config_file): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() project = Project(project_path) - helpers.update_project_environment(project, 'default', {'skip-install': True, **project.config.envs['default']}) - helpers.update_project_environment(project, 'test', {'matrix': [{'version': ['9000', '42']}]}) + helpers.update_project_environment(project, "default", {"skip-install": True, **project.config.envs["default"]}) + helpers.update_project_environment(project, "test", {"matrix": [{"version": ["9000", "42"]}]}) with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): result = hatch( - 'run', - '+version=9000', - 'test:python', - '-c', + "run", + "+version=9000", + "test:python", + "-c", "import os,sys;open('test.txt', 'a').write(sys.executable+os.linesep[-1])", ) @@ -1618,10 +1618,10 @@ def test_matrix_variable_selection_inclusion(hatch, helpers, temp_dir, config_fi Checking dependencies """ ) - output_file = project_path / 'test.txt' + output_file = project_path / "test.txt" assert output_file.is_file() - env_data_path = data_path / 'env' / 'virtual' + env_data_path = data_path / "env" / "virtual" assert env_data_path.is_dir() project_data_path = env_data_path / project_path.name @@ -1637,37 +1637,37 @@ def test_matrix_variable_selection_inclusion(hatch, helpers, temp_dir, config_fi assert len(env_dirs) == 1 env_path = env_dirs[0] - assert env_path.name == 'test.9000' + assert env_path.name == "test.9000" python_path = str(output_file.read_text()).strip() assert str(env_path) in python_path def test_matrix_variable_selection_exclusion(hatch, helpers, temp_dir, config_file): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() project = Project(project_path) - helpers.update_project_environment(project, 'default', {'skip-install': True, **project.config.envs['default']}) - helpers.update_project_environment(project, 'test', {'matrix': [{'version': ['9000', '42']}]}) + helpers.update_project_environment(project, "default", {"skip-install": True, **project.config.envs["default"]}) + helpers.update_project_environment(project, "test", {"matrix": [{"version": ["9000", "42"]}]}) with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): result = hatch( - 'run', - '-version=9000', - 'test:python', - '-c', + "run", + "-version=9000", + "test:python", + "-c", "import os,sys;open('test.txt', 'a').write(sys.executable+os.linesep[-1])", ) @@ -1679,10 +1679,10 @@ def test_matrix_variable_selection_exclusion(hatch, helpers, temp_dir, config_fi Checking dependencies """ ) - output_file = project_path / 'test.txt' + output_file = project_path / "test.txt" assert output_file.is_file() - env_data_path = data_path / 'env' / 'virtual' + env_data_path = data_path / "env" / "virtual" assert env_data_path.is_dir() project_data_path = env_data_path / project_path.name @@ -1698,37 +1698,37 @@ def test_matrix_variable_selection_exclusion(hatch, helpers, temp_dir, config_fi assert len(env_dirs) == 1 env_path = env_dirs[0] - assert env_path.name == 'test.42' + assert env_path.name == "test.42" python_path = str(output_file.read_text()).strip() assert str(env_path) in python_path def test_matrix_variable_selection_exclude_all(hatch, helpers, temp_dir, config_file): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() project = Project(project_path) - helpers.update_project_environment(project, 'default', {'skip-install': True, **project.config.envs['default']}) - helpers.update_project_environment(project, 'test', {'matrix': [{'version': ['9000', '42']}]}) + helpers.update_project_environment(project, "default", {"skip-install": True, **project.config.envs["default"]}) + helpers.update_project_environment(project, "test", {"matrix": [{"version": ["9000", "42"]}]}) with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): result = hatch( - 'run', - '-version', - 'test:python', - '-c', + "run", + "-version", + "test:python", + "-c", "import os,sys;open('test.txt', 'a').write(sys.executable+os.linesep[-1])", ) @@ -1741,30 +1741,30 @@ def test_matrix_variable_selection_exclude_all(hatch, helpers, temp_dir, config_ def test_matrix_variable_selection_include_none(hatch, helpers, temp_dir, config_file): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() project = Project(project_path) - helpers.update_project_environment(project, 'default', {'skip-install': True, **project.config.envs['default']}) - helpers.update_project_environment(project, 'test', {'matrix': [{'version': ['9000', '42']}]}) + helpers.update_project_environment(project, "default", {"skip-install": True, **project.config.envs["default"]}) + helpers.update_project_environment(project, "test", {"matrix": [{"version": ["9000", "42"]}]}) with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): result = hatch( - 'run', - '+version=3.14', - 'test:python', - '-c', + "run", + "+version=3.14", + "test:python", + "-c", "import os,sys;open('test.txt', 'a').write(sys.executable+os.linesep[-1])", ) @@ -1777,32 +1777,32 @@ def test_matrix_variable_selection_include_none(hatch, helpers, temp_dir, config def test_matrix_variable_selection_inclusion_multiple_variables(hatch, helpers, temp_dir, config_file): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() project = Project(project_path) - helpers.update_project_environment(project, 'default', {'skip-install': True, **project.config.envs['default']}) + helpers.update_project_environment(project, "default", {"skip-install": True, **project.config.envs["default"]}) helpers.update_project_environment( - project, 'test', {'matrix': [{'version1': ['9000', '42'], 'version2': ['3.14']}]} + project, "test", {"matrix": [{"version1": ["9000", "42"], "version2": ["3.14"]}]} ) with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): result = hatch( - 'run', - '+version1=9000', - 'test:python', - '-c', + "run", + "+version1=9000", + "test:python", + "-c", "import os,sys;open('test.txt', 'a').write(sys.executable+os.linesep[-1])", ) @@ -1814,10 +1814,10 @@ def test_matrix_variable_selection_inclusion_multiple_variables(hatch, helpers, Checking dependencies """ ) - output_file = project_path / 'test.txt' + output_file = project_path / "test.txt" assert output_file.is_file() - env_data_path = data_path / 'env' / 'virtual' + env_data_path = data_path / "env" / "virtual" assert env_data_path.is_dir() project_data_path = env_data_path / project_path.name @@ -1833,45 +1833,45 @@ def test_matrix_variable_selection_inclusion_multiple_variables(hatch, helpers, assert len(env_dirs) == 1 env_path = env_dirs[0] - assert env_path.name == 'test.9000-3.14' + assert env_path.name == "test.9000-3.14" python_path = str(output_file.read_text()).strip() assert str(env_path) in python_path def test_context_formatting_recursion(hatch, helpers, temp_dir, config_file): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() project = Project(project_path) helpers.update_project_environment( project, - 'default', + "default", { - 'skip-install': True, - 'scripts': {'py': 'python -c "{env:FOO:{env:BAR:{env:BAZ}}}"'}, - **project.config.envs['default'], + "skip-install": True, + "scripts": {"py": 'python -c "{env:FOO:{env:BAR:{env:BAZ}}}"'}, + **project.config.envs["default"], }, ) with project_path.as_cwd( env_vars={ ConfigEnvVars.DATA: str(data_path), - 'BAZ': "import pathlib,sys;pathlib.Path('test.txt').write_text(sys.executable)", + "BAZ": "import pathlib,sys;pathlib.Path('test.txt').write_text(sys.executable)", } ): - result = hatch('run', 'py') + result = hatch("run", "py") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -1880,10 +1880,10 @@ def test_context_formatting_recursion(hatch, helpers, temp_dir, config_file): Checking dependencies """ ) - output_file = project_path / 'test.txt' + output_file = project_path / "test.txt" assert output_file.is_file() - env_data_path = data_path / 'env' / 'virtual' + env_data_path = data_path / "env" / "virtual" assert env_data_path.is_dir() project_data_path = env_data_path / project_path.name @@ -1906,18 +1906,18 @@ def test_context_formatting_recursion(hatch, helpers, temp_dir, config_file): def test_plugin_dependencies_unmet(hatch, helpers, temp_dir, config_file, mock_plugin_installation): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() dependency = os.urandom(16).hex() @@ -1931,10 +1931,10 @@ def test_plugin_dependencies_unmet(hatch, helpers, temp_dir, config_file, mock_p ) project = Project(project_path) - helpers.update_project_environment(project, 'default', {'skip-install': True, **project.config.envs['default']}) + helpers.update_project_environment(project, "default", {"skip-install": True, **project.config.envs["default"]}) with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('run', 'python', '-c', "import pathlib,sys;pathlib.Path('test.txt').write_text(sys.executable)") + result = hatch("run", "python", "-c", "import pathlib,sys;pathlib.Path('test.txt').write_text(sys.executable)") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -1946,10 +1946,10 @@ def test_plugin_dependencies_unmet(hatch, helpers, temp_dir, config_file, mock_p ) helpers.assert_plugin_installation(mock_plugin_installation, [dependency]) - output_file = project_path / 'test.txt' + output_file = project_path / "test.txt" assert output_file.is_file() - env_data_path = data_path / 'env' / 'virtual' + env_data_path = data_path / "env" / "virtual" assert env_data_path.is_dir() project_data_path = env_data_path / project_path.name @@ -1973,34 +1973,34 @@ def test_plugin_dependencies_unmet(hatch, helpers, temp_dir, config_file, mock_p @pytest.mark.requires_internet def test_install_python_specific(hatch, helpers, temp_dir, config_file, mocker, available_python_version): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() project = Project(project_path) helpers.update_project_environment( project, - 'default', - {'skip-install': True, 'python': available_python_version, **project.config.envs['default']}, + "default", + {"skip-install": True, "python": available_python_version, **project.config.envs["default"]}, ) - mocker.patch('hatch.env.virtual.VirtualEnvironment._interpreter_is_compatible', return_value=False) - manager = PythonManager(data_path / 'env' / 'virtual' / '.pythons') + mocker.patch("hatch.env.virtual.VirtualEnvironment._interpreter_is_compatible", return_value=False) + manager = PythonManager(data_path / "env" / "virtual" / ".pythons") assert not manager.get_installed() with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): result = hatch( - 'run', 'python', '-c', "import os,sys;open('test.txt', 'a').write(sys.executable+os.linesep[-1])" + "run", "python", "-c", "import os,sys;open('test.txt', 'a').write(sys.executable+os.linesep[-1])" ) assert result.exit_code == 0, result.output @@ -2011,10 +2011,10 @@ def test_install_python_specific(hatch, helpers, temp_dir, config_file, mocker, Checking dependencies """ ) - output_file = project_path / 'test.txt' + output_file = project_path / "test.txt" assert output_file.is_file() - env_data_path = data_path / 'env' / 'virtual' + env_data_path = data_path / "env" / "virtual" assert env_data_path.is_dir() project_data_path = env_data_path / project_path.name @@ -2040,39 +2040,39 @@ def test_install_python_specific(hatch, helpers, temp_dir, config_file, mocker, @pytest.mark.requires_internet def test_update_python_specific(hatch, helpers, temp_dir, config_file, mocker, available_python_version): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() project = Project(project_path) helpers.update_project_environment( project, - 'default', - {'skip-install': True, 'python': available_python_version, **project.config.envs['default']}, + "default", + {"skip-install": True, "python": available_python_version, **project.config.envs["default"]}, ) - install_dir = data_path / 'env' / 'virtual' / '.pythons' + install_dir = data_path / "env" / "virtual" / ".pythons" manager = PythonManager(install_dir) dist = manager.install(available_python_version) helpers.downgrade_distribution_metadata(install_dir / available_python_version) mocker.patch( - 'hatch.env.virtual.VirtualEnvironment._interpreter_is_compatible', + "hatch.env.virtual.VirtualEnvironment._interpreter_is_compatible", side_effect=lambda interpreter: Path(interpreter.executable) == dist.python_path, ) with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): result = hatch( - 'run', 'python', '-c', "import os,sys;open('test.txt', 'a').write(sys.executable+os.linesep[-1])" + "run", "python", "-c", "import os,sys;open('test.txt', 'a').write(sys.executable+os.linesep[-1])" ) assert result.exit_code == 0, result.output @@ -2083,10 +2083,10 @@ def test_update_python_specific(hatch, helpers, temp_dir, config_file, mocker, a Checking dependencies """ ) - output_file = project_path / 'test.txt' + output_file = project_path / "test.txt" assert output_file.is_file() - env_data_path = data_path / 'env' / 'virtual' + env_data_path = data_path / "env" / "virtual" assert env_data_path.is_dir() project_data_path = env_data_path / project_path.name @@ -2110,30 +2110,30 @@ def test_update_python_specific(hatch, helpers, temp_dir, config_file, mocker, a @pytest.mark.requires_internet def test_install_python_max_compatible(hatch, helpers, temp_dir, config_file, mocker, available_python_version): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() project = Project(project_path) - helpers.update_project_environment(project, 'default', {'skip-install': True, **project.config.envs['default']}) + helpers.update_project_environment(project, "default", {"skip-install": True, **project.config.envs["default"]}) - mocker.patch('hatch.env.virtual.VirtualEnvironment._interpreter_is_compatible', return_value=False) - manager = PythonManager(data_path / 'env' / 'virtual' / '.pythons') + mocker.patch("hatch.env.virtual.VirtualEnvironment._interpreter_is_compatible", return_value=False) + manager = PythonManager(data_path / "env" / "virtual" / ".pythons") assert not manager.get_installed() with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): result = hatch( - 'run', 'python', '-c', "import os,sys;open('test.txt', 'a').write(sys.executable+os.linesep[-1])" + "run", "python", "-c", "import os,sys;open('test.txt', 'a').write(sys.executable+os.linesep[-1])" ) assert result.exit_code == 0, result.output @@ -2144,10 +2144,10 @@ def test_install_python_max_compatible(hatch, helpers, temp_dir, config_file, mo Checking dependencies """ ) - output_file = project_path / 'test.txt' + output_file = project_path / "test.txt" assert output_file.is_file() - env_data_path = data_path / 'env' / 'virtual' + env_data_path = data_path / "env" / "virtual" assert env_data_path.is_dir() project_data_path = env_data_path / project_path.name @@ -2173,35 +2173,35 @@ def test_install_python_max_compatible(hatch, helpers, temp_dir, config_file, mo @pytest.mark.requires_internet def test_update_python_max_compatible(hatch, helpers, temp_dir, config_file, mocker, available_python_version): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() project = Project(project_path) - helpers.update_project_environment(project, 'default', {'skip-install': True, **project.config.envs['default']}) + helpers.update_project_environment(project, "default", {"skip-install": True, **project.config.envs["default"]}) - install_dir = data_path / 'env' / 'virtual' / '.pythons' + install_dir = data_path / "env" / "virtual" / ".pythons" manager = PythonManager(install_dir) dist = manager.install(available_python_version) helpers.downgrade_distribution_metadata(install_dir / available_python_version) mocker.patch( - 'hatch.env.virtual.VirtualEnvironment._interpreter_is_compatible', + "hatch.env.virtual.VirtualEnvironment._interpreter_is_compatible", side_effect=lambda interpreter: Path(interpreter.executable) == dist.python_path, ) with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): result = hatch( - 'run', 'python', '-c', "import os,sys;open('test.txt', 'a').write(sys.executable+os.linesep[-1])" + "run", "python", "-c", "import os,sys;open('test.txt', 'a').write(sys.executable+os.linesep[-1])" ) assert result.exit_code == 0, result.output @@ -2212,10 +2212,10 @@ def test_update_python_max_compatible(hatch, helpers, temp_dir, config_file, moc Checking dependencies """ ) - output_file = project_path / 'test.txt' + output_file = project_path / "test.txt" assert output_file.is_file() - env_data_path = data_path / 'env' / 'virtual' + env_data_path = data_path / "env" / "virtual" assert env_data_path.is_dir() project_data_path = env_data_path / project_path.name @@ -2241,30 +2241,30 @@ def test_update_python_max_compatible(hatch, helpers, temp_dir, config_file, moc def test_python_installation_with_metadata_hook( hatch, helpers, temp_dir, config_file, mocker, available_python_version ): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() project = Project(project_path) config = dict(project.raw_config) - config['build-system']['requires'].append('foo') - config['tool']['hatch']['metadata'] = {'hooks': {'custom': {'dependencies': ['binary']}}} + config["build-system"]["requires"].append("foo") + config["tool"]["hatch"]["metadata"] = {"hooks": {"custom": {"dependencies": ["binary"]}}} project.save_config(config) helpers.update_project_environment( project, - 'default', - {'skip-install': True, 'python': available_python_version, **project.config.envs['default']}, + "default", + {"skip-install": True, "python": available_python_version, **project.config.envs["default"]}, ) build_script = project_path / DEFAULT_BUILD_SCRIPT @@ -2280,13 +2280,13 @@ def update(self, metadata): ) ) - mocker.patch('hatch.env.virtual.VirtualEnvironment._interpreter_is_compatible', return_value=False) - manager = PythonManager(data_path / 'env' / 'virtual' / '.pythons') + mocker.patch("hatch.env.virtual.VirtualEnvironment._interpreter_is_compatible", return_value=False) + manager = PythonManager(data_path / "env" / "virtual" / ".pythons") assert not manager.get_installed() with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): result = hatch( - 'run', 'python', '-c', "import os,sys;open('test.txt', 'a').write(sys.executable+os.linesep[-1])" + "run", "python", "-c", "import os,sys;open('test.txt', 'a').write(sys.executable+os.linesep[-1])" ) assert result.exit_code == 0, result.output @@ -2297,10 +2297,10 @@ def update(self, metadata): Checking dependencies """ ) - output_file = project_path / 'test.txt' + output_file = project_path / "test.txt" assert output_file.is_file() - env_data_path = data_path / 'env' / 'virtual' + env_data_path = data_path / "env" / "virtual" assert env_data_path.is_dir() project_data_path = env_data_path / project_path.name @@ -2327,26 +2327,26 @@ def update(self, metadata): class TestScriptRunner: @pytest.mark.requires_internet def test_not_file(self, hatch, helpers, temp_dir): - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() project = Project(project_path) helpers.update_project_environment( project, - 'default', - {'skip-install': True, 'scripts': {'script.py': 'python -c {args}'}, **project.config.envs['default']}, + "default", + {"skip-install": True, "scripts": {"script.py": "python -c {args}"}, **project.config.envs["default"]}, ) with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('run', 'script.py', "import pathlib,sys;pathlib.Path('test.txt').write_text(sys.executable)") + result = hatch("run", "script.py", "import pathlib,sys;pathlib.Path('test.txt').write_text(sys.executable)") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -2355,10 +2355,10 @@ def test_not_file(self, hatch, helpers, temp_dir): Checking dependencies """ ) - output_file = project_path / 'test.txt' + output_file = project_path / "test.txt" assert output_file.is_file() - env_data_path = data_path / 'env' / 'virtual' + env_data_path = data_path / "env" / "virtual" assert env_data_path.is_dir() project_data_path = env_data_path / project_path.name @@ -2381,9 +2381,9 @@ def test_not_file(self, hatch, helpers, temp_dir): @pytest.mark.requires_internet def test_dependencies(self, hatch, helpers, temp_dir): - data_path = temp_dir / 'data' + data_path = temp_dir / "data" data_path.mkdir() - script = (temp_dir / 'script.py').resolve() + script = (temp_dir / "script.py").resolve() script.write_text( helpers.dedent( """ @@ -2405,7 +2405,7 @@ def test_dependencies(self, hatch, helpers, temp_dir): ) with temp_dir.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('run', 'script.py') + result = hatch("run", "script.py") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -2415,10 +2415,10 @@ def test_dependencies(self, hatch, helpers, temp_dir): Syncing dependencies """ ) - output_file = temp_dir / 'test.txt' + output_file = temp_dir / "test.txt" assert output_file.is_file() - env_data_path = data_path / 'env' / 'virtual' / '.scripts' + env_data_path = data_path / "env" / "virtual" / ".scripts" assert env_data_path.is_dir() env_path = env_data_path / script.id @@ -2434,9 +2434,9 @@ def test_dependencies(self, hatch, helpers, temp_dir): @pytest.mark.requires_internet def test_dependencies_from_tool_config(self, hatch, helpers, temp_dir): - data_path = temp_dir / 'data' + data_path = temp_dir / "data" data_path.mkdir() - script = (temp_dir / 'script.py').resolve() + script = (temp_dir / "script.py").resolve() script.write_text( helpers.dedent( """ @@ -2461,7 +2461,7 @@ def test_dependencies_from_tool_config(self, hatch, helpers, temp_dir): ) with temp_dir.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('run', 'script.py') + result = hatch("run", "script.py") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -2471,10 +2471,10 @@ def test_dependencies_from_tool_config(self, hatch, helpers, temp_dir): Syncing dependencies """ ) - output_file = temp_dir / 'test.txt' + output_file = temp_dir / "test.txt" assert output_file.is_file() - env_data_path = data_path / 'env' / 'virtual' / '.scripts' + env_data_path = data_path / "env" / "virtual" / ".scripts" assert env_data_path.is_dir() env_path = env_data_path / script.id @@ -2489,9 +2489,9 @@ def test_dependencies_from_tool_config(self, hatch, helpers, temp_dir): assert unit_conversion == "(1.0, 'KiB')" def test_unsupported_python_version(self, hatch, helpers, temp_dir): - data_path = temp_dir / 'data' + data_path = temp_dir / "data" data_path.mkdir() - script = (temp_dir / 'script.py').resolve() + script = (temp_dir / "script.py").resolve() script.write_text( helpers.dedent( """ @@ -2507,7 +2507,7 @@ def test_unsupported_python_version(self, hatch, helpers, temp_dir): ) with temp_dir.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('run', 'script.py') + result = hatch("run", "script.py") assert result.exit_code == 1, result.output assert result.output == helpers.dedent( @@ -2518,9 +2518,9 @@ def test_unsupported_python_version(self, hatch, helpers, temp_dir): @pytest.mark.requires_internet def test_python_version_constraint(self, hatch, helpers, temp_dir): - data_path = temp_dir / 'data' + data_path = temp_dir / "data" data_path.mkdir() - script = (temp_dir / 'script.py').resolve() + script = (temp_dir / "script.py").resolve() # Cap the range at the current minor version so that the current Python # will be used and distributions don't have to be downloaded @@ -2542,7 +2542,7 @@ def test_python_version_constraint(self, hatch, helpers, temp_dir): ) with temp_dir.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('run', 'script.py') + result = hatch("run", "script.py") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -2551,10 +2551,10 @@ def test_python_version_constraint(self, hatch, helpers, temp_dir): Checking dependencies """ ) - output_file = temp_dir / 'test.txt' + output_file = temp_dir / "test.txt" assert output_file.is_file() - env_data_path = data_path / 'env' / 'virtual' / '.scripts' + env_data_path = data_path / "env" / "virtual" / ".scripts" assert env_data_path.is_dir() env_path = env_data_path / script.id @@ -2566,9 +2566,9 @@ def test_python_version_constraint(self, hatch, helpers, temp_dir): assert data_path in executable.parents def test_python_version_constraint_from_tool_config(self, hatch, helpers, temp_dir): - data_path = temp_dir / 'data' + data_path = temp_dir / "data" data_path.mkdir() - script = (temp_dir / 'script.py').resolve() + script = (temp_dir / "script.py").resolve() # Use the current minor version so that the current Python # will be used and distributions don't have to be downloaded @@ -2592,7 +2592,7 @@ def test_python_version_constraint_from_tool_config(self, hatch, helpers, temp_d ) with temp_dir.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('run', 'script.py') + result = hatch("run", "script.py") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -2601,10 +2601,10 @@ def test_python_version_constraint_from_tool_config(self, hatch, helpers, temp_d Checking dependencies """ ) - output_file = temp_dir / 'test.txt' + output_file = temp_dir / "test.txt" assert output_file.is_file() - env_data_path = data_path / 'env' / 'virtual' / '.scripts' + env_data_path = data_path / "env" / "virtual" / ".scripts" assert env_data_path.is_dir() env_path = env_data_path / script.id diff --git a/tests/cli/self/test_report.py b/tests/cli/self/test_report.py index 2f4727467..327c2a376 100644 --- a/tests/cli/self/test_report.py +++ b/tests/cli/self/test_report.py @@ -8,7 +8,7 @@ from hatch._version import __version__ from hatch.utils.structures import EnvVars -URL = 'https://github.com/pypa/hatch/issues/new?body=' +URL = "https://github.com/pypa/hatch/issues/new?body=" STATIC_BODY = """\ ## Current behavior @@ -34,8 +34,8 @@ def assert_call(open_new_tab, expected_body): class TestDefault: def test_open(self, hatch, mocker, platform): - open_new_tab = mocker.patch('webbrowser.open_new_tab') - result = hatch(os.environ['PYAPP_COMMAND_NAME'], 'report') + open_new_tab = mocker.patch("webbrowser.open_new_tab") + result = hatch(os.environ["PYAPP_COMMAND_NAME"], "report") assert result.exit_code == 0, result.output assert not result.output @@ -51,7 +51,7 @@ def test_open(self, hatch, mocker, platform): - Platform: {platform.display_name} - Python version: ``` -{indent(sys.version, ' ' * 4)} +{indent(sys.version, " " * 4)} ``` ### Configuration @@ -65,7 +65,7 @@ def test_open(self, hatch, mocker, platform): assert_call(open_new_tab, expected_body) def test_no_open(self, hatch, platform): - result = hatch(os.environ['PYAPP_COMMAND_NAME'], 'report', '--no-open') + result = hatch(os.environ["PYAPP_COMMAND_NAME"], "report", "--no-open") assert result.exit_code == 0, result.output assert result.output.startswith(URL) @@ -84,7 +84,7 @@ def test_no_open(self, hatch, platform): - Platform: {platform.display_name} - Python version: ``` -{indent(sys.version, ' ' * 4)} +{indent(sys.version, " " * 4)} ``` ### Configuration @@ -98,13 +98,13 @@ def test_no_open(self, hatch, platform): def test_binary(hatch, mocker, platform, temp_dir): - mock_executable = temp_dir / 'exe' + mock_executable = temp_dir / "exe" mock_executable.touch() - mocker.patch('sys.executable', str(mock_executable)) - mocker.patch('platformdirs.user_data_dir', return_value=str(temp_dir)) - open_new_tab = mocker.patch('webbrowser.open_new_tab') + mocker.patch("sys.executable", str(mock_executable)) + mocker.patch("platformdirs.user_data_dir", return_value=str(temp_dir)) + open_new_tab = mocker.patch("webbrowser.open_new_tab") - result = hatch(os.environ['PYAPP_COMMAND_NAME'], 'report') + result = hatch(os.environ["PYAPP_COMMAND_NAME"], "report") assert result.exit_code == 0, result.output assert not result.output @@ -120,7 +120,7 @@ def test_binary(hatch, mocker, platform, temp_dir): - Platform: {platform.display_name} - Python version: ``` -{indent(sys.version, ' ' * 4)} +{indent(sys.version, " " * 4)} ``` ### Configuration @@ -135,14 +135,14 @@ def test_binary(hatch, mocker, platform, temp_dir): def test_pipx(hatch, mocker, platform, temp_dir): - mock_executable = temp_dir / '.local' / 'pipx' / 'venvs' / 'exe' + mock_executable = temp_dir / ".local" / "pipx" / "venvs" / "exe" mock_executable.parent.ensure_dir_exists() mock_executable.touch() - mocker.patch('sys.executable', str(mock_executable)) - mocker.patch('pathlib.Path.home', return_value=temp_dir) - open_new_tab = mocker.patch('webbrowser.open_new_tab') + mocker.patch("sys.executable", str(mock_executable)) + mocker.patch("pathlib.Path.home", return_value=temp_dir) + open_new_tab = mocker.patch("webbrowser.open_new_tab") - result = hatch(os.environ['PYAPP_COMMAND_NAME'], 'report') + result = hatch(os.environ["PYAPP_COMMAND_NAME"], "report") assert result.exit_code == 0, result.output assert not result.output @@ -158,7 +158,7 @@ def test_pipx(hatch, mocker, platform, temp_dir): - Platform: {platform.display_name} - Python version: ``` -{indent(sys.version, ' ' * 4)} +{indent(sys.version, " " * 4)} ``` ### Configuration @@ -173,14 +173,14 @@ def test_pipx(hatch, mocker, platform, temp_dir): def test_system(hatch, mocker, platform, temp_dir): - indicator = temp_dir / 'EXTERNALLY-MANAGED' + indicator = temp_dir / "EXTERNALLY-MANAGED" indicator.touch() - mocker.patch('sysconfig.get_path', return_value=str(temp_dir)) - mocker.patch('sys.prefix', 'foo') - mocker.patch('sys.base_prefix', 'foo') - open_new_tab = mocker.patch('webbrowser.open_new_tab') + mocker.patch("sysconfig.get_path", return_value=str(temp_dir)) + mocker.patch("sys.prefix", "foo") + mocker.patch("sys.base_prefix", "foo") + open_new_tab = mocker.patch("webbrowser.open_new_tab") - result = hatch(os.environ['PYAPP_COMMAND_NAME'], 'report') + result = hatch(os.environ["PYAPP_COMMAND_NAME"], "report") assert result.exit_code == 0, result.output assert not result.output @@ -196,7 +196,7 @@ def test_system(hatch, mocker, platform, temp_dir): - Platform: {platform.display_name} - Python version: ``` -{indent(sys.version, ' ' * 4)} +{indent(sys.version, " " * 4)} ``` ### Configuration @@ -212,13 +212,13 @@ def test_system(hatch, mocker, platform, temp_dir): @pytest.mark.requires_windows def test_windows_store(hatch, mocker, platform, temp_dir): - mock_executable = temp_dir / 'WindowsApps' / 'python.exe' + mock_executable = temp_dir / "WindowsApps" / "python.exe" mock_executable.parent.ensure_dir_exists() mock_executable.touch() - mocker.patch('sys.executable', str(mock_executable)) - open_new_tab = mocker.patch('webbrowser.open_new_tab') + mocker.patch("sys.executable", str(mock_executable)) + open_new_tab = mocker.patch("webbrowser.open_new_tab") - result = hatch(os.environ['PYAPP_COMMAND_NAME'], 'report') + result = hatch(os.environ["PYAPP_COMMAND_NAME"], "report") assert result.exit_code == 0, result.output assert not result.output @@ -234,7 +234,7 @@ def test_windows_store(hatch, mocker, platform, temp_dir): - Platform: {platform.display_name} - Python version: ``` -{indent(sys.version, ' ' * 4)} +{indent(sys.version, " " * 4)} ``` ### Configuration @@ -250,14 +250,14 @@ def test_windows_store(hatch, mocker, platform, temp_dir): @pytest.mark.requires_unix def test_pyenv(hatch, mocker, platform, temp_dir): - mock_executable = temp_dir / 'exe' + mock_executable = temp_dir / "exe" mock_executable.parent.ensure_dir_exists() mock_executable.touch() - mocker.patch('sys.executable', str(mock_executable)) - open_new_tab = mocker.patch('webbrowser.open_new_tab') + mocker.patch("sys.executable", str(mock_executable)) + open_new_tab = mocker.patch("webbrowser.open_new_tab") - with EnvVars({'PYENV_ROOT': str(temp_dir)}): - result = hatch(os.environ['PYAPP_COMMAND_NAME'], 'report') + with EnvVars({"PYENV_ROOT": str(temp_dir)}): + result = hatch(os.environ["PYAPP_COMMAND_NAME"], "report") assert result.exit_code == 0, result.output assert not result.output @@ -273,7 +273,7 @@ def test_pyenv(hatch, mocker, platform, temp_dir): - Platform: {platform.display_name} - Python version: ``` -{indent(sys.version, ' ' * 4)} +{indent(sys.version, " " * 4)} ``` ### Configuration diff --git a/tests/cli/self/test_self.py b/tests/cli/self/test_self.py index 3059905f5..11637a1a9 100644 --- a/tests/cli/self/test_self.py +++ b/tests/cli/self/test_self.py @@ -2,6 +2,6 @@ def test(hatch): - result = hatch(os.environ['PYAPP_COMMAND_NAME'], '-h') + result = hatch(os.environ["PYAPP_COMMAND_NAME"], "-h") assert result.exit_code == 0, result.output diff --git a/tests/cli/status/test_status.py b/tests/cli/status/test_status.py index 30da68529..58fe951a7 100644 --- a/tests/cli/status/test_status.py +++ b/tests/cli/status/test_status.py @@ -7,7 +7,7 @@ class TestModeLocalDefault: def test_no_project(self, hatch, isolation, config_file, helpers): - result = hatch('status') + result = hatch("status") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -19,11 +19,11 @@ def test_no_project(self, hatch, isolation, config_file, helpers): ) def test_found_project(self, hatch, temp_dir, config_file, helpers): - project_file = temp_dir / 'pyproject.toml' + project_file = temp_dir / "pyproject.toml" project_file.touch() with temp_dir.as_cwd(): - result = hatch('status') + result = hatch("status") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -36,16 +36,16 @@ def test_found_project(self, hatch, temp_dir, config_file, helpers): class TestProjectExplicit: - @pytest.mark.parametrize('file_name', ['pyproject.toml', 'setup.py']) + @pytest.mark.parametrize("file_name", ["pyproject.toml", "setup.py"]) def test_found_project_flag(self, hatch, temp_dir, config_file, helpers, file_name): project_file = temp_dir / file_name project_file.touch() - project = 'foo' + project = "foo" config_file.model.projects = {project: str(temp_dir)} config_file.save() - result = hatch('-p', project, 'status') + result = hatch("-p", project, "status") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -56,17 +56,17 @@ def test_found_project_flag(self, hatch, temp_dir, config_file, helpers, file_na """ ) - @pytest.mark.parametrize('file_name', ['pyproject.toml', 'setup.py']) + @pytest.mark.parametrize("file_name", ["pyproject.toml", "setup.py"]) def test_found_project_env(self, hatch, temp_dir, config_file, helpers, file_name): project_file = temp_dir / file_name project_file.touch() - project = 'foo' + project = "foo" config_file.model.projects = {project: str(temp_dir)} config_file.save() with EnvVars({ConfigEnvVars.PROJECT: project}): - result = hatch('status') + result = hatch("status") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -78,30 +78,30 @@ def test_found_project_env(self, hatch, temp_dir, config_file, helpers, file_nam ) def test_unknown_project(self, hatch): - project = 'foo' - result = hatch('-p', project, 'status') + project = "foo" + result = hatch("-p", project, "status") assert result.exit_code == 1 - assert result.output == f'Unable to locate project {project}\n' + assert result.output == f"Unable to locate project {project}\n" def test_not_a_project(self, hatch, temp_dir, config_file): - project = 'foo' + project = "foo" config_file.model.project = project config_file.model.projects = {project: str(temp_dir)} config_file.save() - result = hatch('-p', project, 'status') + result = hatch("-p", project, "status") assert result.exit_code == 1 - assert result.output == f'Unable to locate project {project}\n' + assert result.output == f"Unable to locate project {project}\n" class TestModeProject: def test_no_project(self, hatch, isolation, config_file, helpers): - config_file.model.mode = 'project' + config_file.model.mode = "project" config_file.save() - result = hatch('status') + result = hatch("status") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -114,12 +114,12 @@ def test_no_project(self, hatch, isolation, config_file, helpers): ) def test_unknown_project(self, hatch, isolation, config_file, helpers): - project = 'foo' - config_file.model.mode = 'project' + project = "foo" + config_file.model.mode = "project" config_file.model.project = project config_file.save() - result = hatch('status') + result = hatch("status") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -132,13 +132,13 @@ def test_unknown_project(self, hatch, isolation, config_file, helpers): ) def test_not_a_project(self, hatch, temp_dir, config_file, helpers): - project = 'foo' - config_file.model.mode = 'project' + project = "foo" + config_file.model.mode = "project" config_file.model.project = project config_file.model.projects = {project: str(temp_dir)} config_file.save() - result = hatch('status') + result = hatch("status") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -149,18 +149,18 @@ def test_not_a_project(self, hatch, temp_dir, config_file, helpers): """ ) - @pytest.mark.parametrize('file_name', ['pyproject.toml', 'setup.py']) + @pytest.mark.parametrize("file_name", ["pyproject.toml", "setup.py"]) def test_found_project(self, hatch, temp_dir, config_file, helpers, file_name): project_file = temp_dir / file_name project_file.touch() - project = 'foo' - config_file.model.mode = 'project' + project = "foo" + config_file.model.mode = "project" config_file.model.project = project config_file.model.projects = {project: str(temp_dir)} config_file.save() - result = hatch('status') + result = hatch("status") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -174,10 +174,10 @@ def test_found_project(self, hatch, temp_dir, config_file, helpers, file_name): class TestModeAware: def test_no_detection_no_project(self, hatch, config_file, helpers, isolation): - config_file.model.mode = 'aware' + config_file.model.mode = "aware" config_file.save() - result = hatch('status') + result = hatch("status") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -190,12 +190,12 @@ def test_no_detection_no_project(self, hatch, config_file, helpers, isolation): ) def test_unknown_project(self, hatch, isolation, config_file, helpers): - project = 'foo' + project = "foo" config_file.model.project = project - config_file.model.mode = 'aware' + config_file.model.mode = "aware" config_file.save() - result = hatch('status') + result = hatch("status") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -208,13 +208,13 @@ def test_unknown_project(self, hatch, isolation, config_file, helpers): ) def test_not_a_project(self, hatch, temp_dir, config_file, helpers): - project = 'foo' + project = "foo" config_file.model.project = project config_file.model.projects = {project: str(temp_dir)} - config_file.model.mode = 'aware' + config_file.model.mode = "aware" config_file.save() - result = hatch('status') + result = hatch("status") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -225,18 +225,18 @@ def test_not_a_project(self, hatch, temp_dir, config_file, helpers): """ ) - @pytest.mark.parametrize('file_name', ['pyproject.toml', 'setup.py']) + @pytest.mark.parametrize("file_name", ["pyproject.toml", "setup.py"]) def test_found_project(self, hatch, temp_dir, config_file, helpers, file_name): project_file = temp_dir / file_name project_file.touch() - project = 'foo' + project = "foo" config_file.model.project = project config_file.model.projects = {project: str(temp_dir)} - config_file.model.mode = 'aware' + config_file.model.mode = "aware" config_file.save() - result = hatch('status') + result = hatch("status") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -248,18 +248,18 @@ def test_found_project(self, hatch, temp_dir, config_file, helpers, file_name): ) def test_local_override(self, hatch, temp_dir, config_file, helpers): - project_file = temp_dir / 'pyproject.toml' + project_file = temp_dir / "pyproject.toml" project_file.touch() - project = 'foo' + project = "foo" config_file.model.project = project config_file.model.projects = {project: str(temp_dir)} - config_file.model.mode = 'aware' + config_file.model.mode = "aware" config_file.save() with temp_chdir() as d: - d.joinpath('pyproject.toml').touch() - result = hatch('status') + d.joinpath("pyproject.toml").touch() + result = hatch("status") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( diff --git a/tests/cli/test/test_test.py b/tests/cli/test/test_test.py index 2177c012d..912324366 100644 --- a/tests/cli/test/test_test.py +++ b/tests/cli/test/test_test.py @@ -10,892 +10,892 @@ from hatch.utils.structures import EnvVars -@pytest.fixture(scope='module', autouse=True) +@pytest.fixture(scope="module", autouse=True) def _terminal_width(): - with EnvVars({'COLUMNS': '100'}, exclude=[get_env_var(plugin_name='virtual', option='uv_path')]): + with EnvVars({"COLUMNS": "100"}, exclude=[get_env_var(plugin_name="virtual", option="uv_path")]): yield class TestDefaults: def test_basic(self, hatch, temp_dir, config_file, env_run, mocker): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('test') + result = hatch("test") assert result.exit_code == 0, result.output assert not result.output assert env_run.call_args_list == [ - mocker.call('pytest -p no:randomly tests', shell=True), + mocker.call("pytest -p no:randomly tests", shell=True), ] - assert not (data_path / '.config' / 'coverage').exists() + assert not (data_path / ".config" / "coverage").exists() def test_arguments(self, hatch, temp_dir, config_file, env_run, mocker): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('test', '--', '--flag', '--', 'arg1', 'arg2') + result = hatch("test", "--", "--flag", "--", "arg1", "arg2") assert result.exit_code == 0, result.output assert not result.output assert env_run.call_args_list == [ - mocker.call('pytest -p no:randomly --flag -- arg1 arg2', shell=True), + mocker.call("pytest -p no:randomly --flag -- arg1 arg2", shell=True), ] - assert not (data_path / '.config' / 'coverage').exists() + assert not (data_path / ".config" / "coverage").exists() class TestArguments: def test_default_args(self, hatch, temp_dir, platform, config_file, env_run, mocker): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() project = Project(project_path) config = dict(project.raw_config) - config['tool']['hatch']['envs'] = {'hatch-test': {'default-args': ['tests1', 'foo bar', 'tests2']}} + config["tool"]["hatch"]["envs"] = {"hatch-test": {"default-args": ["tests1", "foo bar", "tests2"]}} project.save_config(config) with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('test') + result = hatch("test") assert result.exit_code == 0, result.output assert not result.output escape_char = '"' if platform.windows else "'" assert env_run.call_args_list == [ - mocker.call(f'pytest -p no:randomly tests1 {escape_char}foo bar{escape_char} tests2', shell=True), + mocker.call(f"pytest -p no:randomly tests1 {escape_char}foo bar{escape_char} tests2", shell=True), ] - assert not (data_path / '.config' / 'coverage').exists() + assert not (data_path / ".config" / "coverage").exists() def test_args_override(self, hatch, temp_dir, config_file, env_run, mocker): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() project = Project(project_path) config = dict(project.raw_config) - config['tool']['hatch']['envs'] = {'hatch-test': {'default-args': ['tests1', 'foo bar', 'tests2']}} + config["tool"]["hatch"]["envs"] = {"hatch-test": {"default-args": ["tests1", "foo bar", "tests2"]}} project.save_config(config) with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('test', '--', '--flag', '--', 'arg1', 'arg2') + result = hatch("test", "--", "--flag", "--", "arg1", "arg2") assert result.exit_code == 0, result.output assert not result.output assert env_run.call_args_list == [ - mocker.call('pytest -p no:randomly --flag -- arg1 arg2', shell=True), + mocker.call("pytest -p no:randomly --flag -- arg1 arg2", shell=True), ] - assert not (data_path / '.config' / 'coverage').exists() + assert not (data_path / ".config" / "coverage").exists() def test_extra_args(self, hatch, temp_dir, config_file, env_run, mocker): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() project = Project(project_path) config = dict(project.raw_config) - config['tool']['hatch']['envs'] = {'hatch-test': {'extra-args': ['-vv', '--print']}} + config["tool"]["hatch"]["envs"] = {"hatch-test": {"extra-args": ["-vv", "--print"]}} project.save_config(config) with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('test') + result = hatch("test") assert result.exit_code == 0, result.output assert not result.output assert env_run.call_args_list == [ - mocker.call('pytest -vv --print -p no:randomly tests', shell=True), + mocker.call("pytest -vv --print -p no:randomly tests", shell=True), ] - assert not (data_path / '.config' / 'coverage').exists() + assert not (data_path / ".config" / "coverage").exists() class TestCoverage: def test_flag(self, hatch, temp_dir, config_file, env_run, mocker): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('test', '--cover') + result = hatch("test", "--cover") assert result.exit_code == 0, result.output assert not result.output assert env_run.call_args_list == [ - mocker.call('coverage run -m pytest -p no:randomly tests', shell=True), - mocker.call('coverage combine', shell=True), - mocker.call('coverage report', shell=True), + mocker.call("coverage run -m pytest -p no:randomly tests", shell=True), + mocker.call("coverage combine", shell=True), + mocker.call("coverage report", shell=True), ] - root_config_path = data_path / '.config' / 'coverage' + root_config_path = data_path / ".config" / "coverage" config_dir = next(root_config_path.iterdir()) coverage_config_file = next(config_dir.iterdir()) assert coverage_config_file.read_text().strip().splitlines()[-2:] == [ - '[tool.coverage.run]', - 'parallel = true', + "[tool.coverage.run]", + "parallel = true", ] def test_flag_with_arguments(self, hatch, temp_dir, config_file, env_run, mocker): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('test', '--cover', '--', '--flag', '--', 'arg1', 'arg2') + result = hatch("test", "--cover", "--", "--flag", "--", "arg1", "arg2") assert result.exit_code == 0, result.output assert not result.output assert env_run.call_args_list == [ - mocker.call('coverage run -m pytest -p no:randomly --flag -- arg1 arg2', shell=True), - mocker.call('coverage combine', shell=True), - mocker.call('coverage report', shell=True), + mocker.call("coverage run -m pytest -p no:randomly --flag -- arg1 arg2", shell=True), + mocker.call("coverage combine", shell=True), + mocker.call("coverage report", shell=True), ] - root_config_path = data_path / '.config' / 'coverage' + root_config_path = data_path / ".config" / "coverage" config_dir = next(root_config_path.iterdir()) coverage_config_file = next(config_dir.iterdir()) assert coverage_config_file.read_text().strip().splitlines()[-2:] == [ - '[tool.coverage.run]', - 'parallel = true', + "[tool.coverage.run]", + "parallel = true", ] def test_quiet_implicitly_enables(self, hatch, temp_dir, config_file, env_run, mocker): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('test', '--cover-quiet') + result = hatch("test", "--cover-quiet") assert result.exit_code == 0, result.output assert not result.output assert env_run.call_args_list == [ - mocker.call('coverage run -m pytest -p no:randomly tests', shell=True), - mocker.call('coverage combine', shell=True), + mocker.call("coverage run -m pytest -p no:randomly tests", shell=True), + mocker.call("coverage combine", shell=True), ] - root_config_path = data_path / '.config' / 'coverage' + root_config_path = data_path / ".config" / "coverage" config_dir = next(root_config_path.iterdir()) coverage_config_file = next(config_dir.iterdir()) assert coverage_config_file.read_text().strip().splitlines()[-2:] == [ - '[tool.coverage.run]', - 'parallel = true', + "[tool.coverage.run]", + "parallel = true", ] def test_legacy_config_define_section(self, hatch, temp_dir, config_file, env_run, mocker): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() - (project_path / '.coveragerc').touch() + (project_path / ".coveragerc").touch() with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('test', '--cover') + result = hatch("test", "--cover") assert result.exit_code == 0, result.output assert not result.output assert env_run.call_args_list == [ - mocker.call('coverage run -m pytest -p no:randomly tests', shell=True), - mocker.call('coverage combine', shell=True), - mocker.call('coverage report', shell=True), + mocker.call("coverage run -m pytest -p no:randomly tests", shell=True), + mocker.call("coverage combine", shell=True), + mocker.call("coverage report", shell=True), ] - root_config_path = data_path / '.config' / 'coverage' + root_config_path = data_path / ".config" / "coverage" config_dir = next(root_config_path.iterdir()) coverage_config_file = next(config_dir.iterdir()) assert coverage_config_file.read_text().strip().splitlines() == [ - '[run]', - 'parallel = true', + "[run]", + "parallel = true", ] def test_legacy_config_enable_parallel(self, hatch, temp_dir, config_file, env_run, mocker): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() - (project_path / '.coveragerc').write_text('[run]\nparallel = false\nbranch = true\n') + (project_path / ".coveragerc").write_text("[run]\nparallel = false\nbranch = true\n") with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('test', '--cover') + result = hatch("test", "--cover") assert result.exit_code == 0, result.output assert not result.output assert env_run.call_args_list == [ - mocker.call('coverage run -m pytest -p no:randomly tests', shell=True), - mocker.call('coverage combine', shell=True), - mocker.call('coverage report', shell=True), + mocker.call("coverage run -m pytest -p no:randomly tests", shell=True), + mocker.call("coverage combine", shell=True), + mocker.call("coverage report", shell=True), ] - root_config_path = data_path / '.config' / 'coverage' + root_config_path = data_path / ".config" / "coverage" config_dir = next(root_config_path.iterdir()) coverage_config_file = next(config_dir.iterdir()) assert coverage_config_file.read_text().strip().splitlines() == [ - '[run]', - 'parallel = true', - 'branch = true', + "[run]", + "parallel = true", + "branch = true", ] class TestRandomize: def test_flag(self, hatch, temp_dir, config_file, env_run, mocker): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('test', '--randomize') + result = hatch("test", "--randomize") assert result.exit_code == 0, result.output assert not result.output assert env_run.call_args_list == [ - mocker.call('pytest tests', shell=True), + mocker.call("pytest tests", shell=True), ] - assert not (data_path / '.config' / 'coverage').exists() + assert not (data_path / ".config" / "coverage").exists() def test_flag_with_arguments(self, hatch, temp_dir, config_file, env_run, mocker): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('test', '--randomize', '--', '--flag', '--', 'arg1', 'arg2') + result = hatch("test", "--randomize", "--", "--flag", "--", "arg1", "arg2") assert result.exit_code == 0, result.output assert not result.output assert env_run.call_args_list == [ - mocker.call('pytest --flag -- arg1 arg2', shell=True), + mocker.call("pytest --flag -- arg1 arg2", shell=True), ] - assert not (data_path / '.config' / 'coverage').exists() + assert not (data_path / ".config" / "coverage").exists() def test_config(self, hatch, temp_dir, config_file, env_run, mocker): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() project = Project(project_path) config = dict(project.raw_config) - config['tool']['hatch']['envs'] = {'hatch-test': {'randomize': True}} + config["tool"]["hatch"]["envs"] = {"hatch-test": {"randomize": True}} project.save_config(config) with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('test') + result = hatch("test") assert result.exit_code == 0, result.output assert not result.output assert env_run.call_args_list == [ - mocker.call('pytest tests', shell=True), + mocker.call("pytest tests", shell=True), ] - assert not (data_path / '.config' / 'coverage').exists() + assert not (data_path / ".config" / "coverage").exists() class TestParallel: def test_flag(self, hatch, temp_dir, config_file, env_run, mocker): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('test', '--parallel') + result = hatch("test", "--parallel") assert result.exit_code == 0, result.output assert not result.output assert env_run.call_args_list == [ - mocker.call('pytest -p no:randomly -n logical tests', shell=True), + mocker.call("pytest -p no:randomly -n logical tests", shell=True), ] - assert not (data_path / '.config' / 'coverage').exists() + assert not (data_path / ".config" / "coverage").exists() def test_flag_with_arguments(self, hatch, temp_dir, config_file, env_run, mocker): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('test', '--parallel', '--', '--flag', '--', 'arg1', 'arg2') + result = hatch("test", "--parallel", "--", "--flag", "--", "arg1", "arg2") assert result.exit_code == 0, result.output assert not result.output assert env_run.call_args_list == [ - mocker.call('pytest -p no:randomly -n logical --flag -- arg1 arg2', shell=True), + mocker.call("pytest -p no:randomly -n logical --flag -- arg1 arg2", shell=True), ] - assert not (data_path / '.config' / 'coverage').exists() + assert not (data_path / ".config" / "coverage").exists() def test_config(self, hatch, temp_dir, config_file, env_run, mocker): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() project = Project(project_path) config = dict(project.raw_config) - config['tool']['hatch']['envs'] = {'hatch-test': {'parallel': True}} + config["tool"]["hatch"]["envs"] = {"hatch-test": {"parallel": True}} project.save_config(config) with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('test') + result = hatch("test") assert result.exit_code == 0, result.output assert not result.output assert env_run.call_args_list == [ - mocker.call('pytest -p no:randomly -n logical tests', shell=True), + mocker.call("pytest -p no:randomly -n logical tests", shell=True), ] - assert not (data_path / '.config' / 'coverage').exists() + assert not (data_path / ".config" / "coverage").exists() class TestRetries: def test_flag(self, hatch, temp_dir, config_file, env_run, mocker): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('test', '--retries', '2') + result = hatch("test", "--retries", "2") assert result.exit_code == 0, result.output assert not result.output assert env_run.call_args_list == [ - mocker.call('pytest -p no:randomly -r aR --reruns 2 tests', shell=True), + mocker.call("pytest -p no:randomly -r aR --reruns 2 tests", shell=True), ] - assert not (data_path / '.config' / 'coverage').exists() + assert not (data_path / ".config" / "coverage").exists() def test_flag_with_arguments(self, hatch, temp_dir, config_file, env_run, mocker): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('test', '--retries', '2', '--', '--flag', '--', 'arg1', 'arg2') + result = hatch("test", "--retries", "2", "--", "--flag", "--", "arg1", "arg2") assert result.exit_code == 0, result.output assert not result.output assert env_run.call_args_list == [ - mocker.call('pytest -p no:randomly -r aR --reruns 2 --flag -- arg1 arg2', shell=True), + mocker.call("pytest -p no:randomly -r aR --reruns 2 --flag -- arg1 arg2", shell=True), ] - assert not (data_path / '.config' / 'coverage').exists() + assert not (data_path / ".config" / "coverage").exists() def test_config(self, hatch, temp_dir, config_file, env_run, mocker): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() project = Project(project_path) config = dict(project.raw_config) - config['tool']['hatch']['envs'] = {'hatch-test': {'retries': 2}} + config["tool"]["hatch"]["envs"] = {"hatch-test": {"retries": 2}} project.save_config(config) with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('test') + result = hatch("test") assert result.exit_code == 0, result.output assert not result.output assert env_run.call_args_list == [ - mocker.call('pytest -p no:randomly -r aR --reruns 2 tests', shell=True), + mocker.call("pytest -p no:randomly -r aR --reruns 2 tests", shell=True), ] - assert not (data_path / '.config' / 'coverage').exists() + assert not (data_path / ".config" / "coverage").exists() class TestRetryDelay: - @pytest.mark.usefixtures('env_run') + @pytest.mark.usefixtures("env_run") def test_no_retries(self, hatch, temp_dir, config_file): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('test', '--retry-delay', '3.14') + result = hatch("test", "--retry-delay", "3.14") assert result.exit_code == 1, result.output - assert result.output == 'The --retry-delay option requires the --retries option to be set as well.\n' + assert result.output == "The --retry-delay option requires the --retries option to be set as well.\n" def test_flag(self, hatch, temp_dir, config_file, env_run, mocker): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('test', '--retries', '2', '--retry-delay', '3.14') + result = hatch("test", "--retries", "2", "--retry-delay", "3.14") assert result.exit_code == 0, result.output assert not result.output assert env_run.call_args_list == [ - mocker.call('pytest -p no:randomly -r aR --reruns 2 --reruns-delay 3.14 tests', shell=True), + mocker.call("pytest -p no:randomly -r aR --reruns 2 --reruns-delay 3.14 tests", shell=True), ] - assert not (data_path / '.config' / 'coverage').exists() + assert not (data_path / ".config" / "coverage").exists() def test_flag_with_arguments(self, hatch, temp_dir, config_file, env_run, mocker): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('test', '--retries', '2', '--retry-delay', '3.14', '--', '--flag', '--', 'arg1', 'arg2') + result = hatch("test", "--retries", "2", "--retry-delay", "3.14", "--", "--flag", "--", "arg1", "arg2") assert result.exit_code == 0, result.output assert not result.output assert env_run.call_args_list == [ - mocker.call('pytest -p no:randomly -r aR --reruns 2 --reruns-delay 3.14 --flag -- arg1 arg2', shell=True), + mocker.call("pytest -p no:randomly -r aR --reruns 2 --reruns-delay 3.14 --flag -- arg1 arg2", shell=True), ] - assert not (data_path / '.config' / 'coverage').exists() + assert not (data_path / ".config" / "coverage").exists() def test_config(self, hatch, temp_dir, config_file, env_run, mocker): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() project = Project(project_path) config = dict(project.raw_config) - config['tool']['hatch']['envs'] = {'hatch-test': {'retry-delay': 1.23}} + config["tool"]["hatch"]["envs"] = {"hatch-test": {"retry-delay": 1.23}} project.save_config(config) with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('test', '--retries', '2') + result = hatch("test", "--retries", "2") assert result.exit_code == 0, result.output assert not result.output assert env_run.call_args_list == [ - mocker.call('pytest -p no:randomly -r aR --reruns 2 --reruns-delay 1.23 tests', shell=True), + mocker.call("pytest -p no:randomly -r aR --reruns 2 --reruns-delay 1.23 tests", shell=True), ] - assert not (data_path / '.config' / 'coverage').exists() + assert not (data_path / ".config" / "coverage").exists() class TestCustomScripts: def test_basic(self, hatch, temp_dir, config_file, env_run, mocker): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() project = Project(project_path) config = dict(project.raw_config) - config['tool']['hatch']['envs'] = { - 'hatch-test': { - 'scripts': { - 'run': 'test', - 'run-cov': 'test with coverage', - 'cov-combine': 'combine coverage', - 'cov-report': 'show coverage', + config["tool"]["hatch"]["envs"] = { + "hatch-test": { + "scripts": { + "run": "test", + "run-cov": "test with coverage", + "cov-combine": "combine coverage", + "cov-report": "show coverage", }, } } project.save_config(config) with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('test') + result = hatch("test") assert result.exit_code == 0, result.output assert not result.output assert env_run.call_args_list == [ - mocker.call('test', shell=True), + mocker.call("test", shell=True), ] - assert not (data_path / '.config' / 'coverage').exists() + assert not (data_path / ".config" / "coverage").exists() def test_coverage(self, hatch, temp_dir, config_file, env_run, mocker): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() project = Project(project_path) config = dict(project.raw_config) - config['tool']['hatch']['envs'] = { - 'hatch-test': { - 'scripts': { - 'run': 'test', - 'run-cov': 'test with coverage', - 'cov-combine': 'combine coverage', - 'cov-report': 'show coverage', + config["tool"]["hatch"]["envs"] = { + "hatch-test": { + "scripts": { + "run": "test", + "run-cov": "test with coverage", + "cov-combine": "combine coverage", + "cov-report": "show coverage", }, } } project.save_config(config) with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('test', '--cover') + result = hatch("test", "--cover") assert result.exit_code == 0, result.output assert not result.output assert env_run.call_args_list == [ - mocker.call('test with coverage', shell=True), - mocker.call('combine coverage', shell=True), - mocker.call('show coverage', shell=True), + mocker.call("test with coverage", shell=True), + mocker.call("combine coverage", shell=True), + mocker.call("show coverage", shell=True), ] - root_config_path = data_path / '.config' / 'coverage' + root_config_path = data_path / ".config" / "coverage" config_dir = next(root_config_path.iterdir()) coverage_config_file = next(config_dir.iterdir()) assert coverage_config_file.read_text().strip().splitlines()[-2:] == [ - '[tool.coverage.run]', - 'parallel = true', + "[tool.coverage.run]", + "parallel = true", ] def test_single(self, hatch, temp_dir, config_file, env_run, mocker): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() project = Project(project_path) config = dict(project.raw_config) - config['tool']['hatch']['envs'] = { - 'hatch-test': { - 'scripts': { - 'run': 'test {env_name}', - 'run-cov': 'test with coverage', - 'cov-combine': 'combine coverage', - 'cov-report': 'show coverage', + config["tool"]["hatch"]["envs"] = { + "hatch-test": { + "scripts": { + "run": "test {env_name}", + "run-cov": "test with coverage", + "cov-combine": "combine coverage", + "cov-report": "show coverage", }, } } project.save_config(config) with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('test') + result = hatch("test") assert result.exit_code == 0, result.output assert not result.output assert env_run.call_args_list == [ - mocker.call(f'test hatch-test.py{".".join(map(str, sys.version_info[:2]))}', shell=True), + mocker.call(f"test hatch-test.py{'.'.join(map(str, sys.version_info[:2]))}", shell=True), ] - assert not (data_path / '.config' / 'coverage').exists() + assert not (data_path / ".config" / "coverage").exists() def test_matrix(self, hatch, temp_dir, config_file, helpers, env_run, mocker): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() project = Project(project_path) config = dict(project.raw_config) - config['tool']['hatch']['envs'] = { - 'hatch-test': { - 'matrix': [{'python': ['3.12', '3.10', '3.8']}], - 'scripts': { - 'run': 'test {env_name}', - 'run-cov': 'test with coverage', - 'cov-combine': 'combine coverage', - 'cov-report': 'show coverage', + config["tool"]["hatch"]["envs"] = { + "hatch-test": { + "matrix": [{"python": ["3.12", "3.10", "3.8"]}], + "scripts": { + "run": "test {env_name}", + "run-cov": "test with coverage", + "cov-combine": "combine coverage", + "cov-report": "show coverage", }, } } project.save_config(config) with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('test', '--all') + result = hatch("test", "--all") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -907,34 +907,34 @@ def test_matrix(self, hatch, temp_dir, config_file, helpers, env_run, mocker): ) assert env_run.call_args_list == [ - mocker.call('test hatch-test.py3.12', shell=True), - mocker.call('test hatch-test.py3.10', shell=True), - mocker.call('test hatch-test.py3.8', shell=True), + mocker.call("test hatch-test.py3.12", shell=True), + mocker.call("test hatch-test.py3.10", shell=True), + mocker.call("test hatch-test.py3.8", shell=True), ] - assert not (data_path / '.config' / 'coverage').exists() + assert not (data_path / ".config" / "coverage").exists() class TestFilters: - @pytest.mark.usefixtures('env_run') - @pytest.mark.parametrize('option', ['--include', '--exclude']) + @pytest.mark.usefixtures("env_run") + @pytest.mark.parametrize("option", ["--include", "--exclude"]) def test_usage_with_all(self, hatch, temp_dir, config_file, helpers, option): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('test', '--all', option, 'py=3.10') + result = hatch("test", "--all", option, "py=3.10") assert result.exit_code == 1, result.output assert result.output == helpers.dedent( @@ -944,37 +944,37 @@ def test_usage_with_all(self, hatch, temp_dir, config_file, helpers, option): ) def test_include(self, hatch, temp_dir, config_file, helpers, env_run, mocker): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() project = Project(project_path) config = dict(project.raw_config) - config['tool']['hatch']['envs'] = { - 'hatch-test': { - 'matrix': [{'python': ['3.12', '3.10', '3.8']}], - 'scripts': { - 'run': 'test {env_name}', - 'run-cov': 'test with coverage', - 'cov-combine': 'combine coverage', - 'cov-report': 'show coverage', + config["tool"]["hatch"]["envs"] = { + "hatch-test": { + "matrix": [{"python": ["3.12", "3.10", "3.8"]}], + "scripts": { + "run": "test {env_name}", + "run-cov": "test with coverage", + "cov-combine": "combine coverage", + "cov-report": "show coverage", }, } } project.save_config(config) with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('test', '-i', 'py=3.10') + result = hatch("test", "-i", "py=3.10") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -984,43 +984,43 @@ def test_include(self, hatch, temp_dir, config_file, helpers, env_run, mocker): ) assert env_run.call_args_list == [ - mocker.call('test hatch-test.py3.10', shell=True), + mocker.call("test hatch-test.py3.10", shell=True), ] - assert not (data_path / '.config' / 'coverage').exists() + assert not (data_path / ".config" / "coverage").exists() def test_exclude(self, hatch, temp_dir, config_file, helpers, env_run, mocker): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() project = Project(project_path) config = dict(project.raw_config) - config['tool']['hatch']['envs'] = { - 'hatch-test': { - 'matrix': [{'python': ['3.12', '3.10', '3.8']}], - 'scripts': { - 'run': 'test {env_name}', - 'run-cov': 'test with coverage', - 'cov-combine': 'combine coverage', - 'cov-report': 'show coverage', + config["tool"]["hatch"]["envs"] = { + "hatch-test": { + "matrix": [{"python": ["3.12", "3.10", "3.8"]}], + "scripts": { + "run": "test {env_name}", + "run-cov": "test with coverage", + "cov-combine": "combine coverage", + "cov-report": "show coverage", }, } } project.save_config(config) with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('test', '-x', 'py=3.10') + result = hatch("test", "-x", "py=3.10") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -1031,44 +1031,44 @@ def test_exclude(self, hatch, temp_dir, config_file, helpers, env_run, mocker): ) assert env_run.call_args_list == [ - mocker.call('test hatch-test.py3.12', shell=True), - mocker.call('test hatch-test.py3.8', shell=True), + mocker.call("test hatch-test.py3.12", shell=True), + mocker.call("test hatch-test.py3.8", shell=True), ] - assert not (data_path / '.config' / 'coverage').exists() + assert not (data_path / ".config" / "coverage").exists() def test_python(self, hatch, temp_dir, config_file, helpers, env_run, mocker): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() project = Project(project_path) config = dict(project.raw_config) - config['tool']['hatch']['envs'] = { - 'hatch-test': { - 'matrix': [{'python': ['3.12', '3.10', '3.8']}], - 'scripts': { - 'run': 'test {env_name}', - 'run-cov': 'test with coverage', - 'cov-combine': 'combine coverage', - 'cov-report': 'show coverage', + config["tool"]["hatch"]["envs"] = { + "hatch-test": { + "matrix": [{"python": ["3.12", "3.10", "3.8"]}], + "scripts": { + "run": "test {env_name}", + "run-cov": "test with coverage", + "cov-combine": "combine coverage", + "cov-report": "show coverage", }, } } project.save_config(config) with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('test', '-py', '3.10') + result = hatch("test", "-py", "3.10") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -1078,46 +1078,46 @@ def test_python(self, hatch, temp_dir, config_file, helpers, env_run, mocker): ) assert env_run.call_args_list == [ - mocker.call('test hatch-test.py3.10', shell=True), + mocker.call("test hatch-test.py3.10", shell=True), ] - assert not (data_path / '.config' / 'coverage').exists() + assert not (data_path / ".config" / "coverage").exists() class TestShow: def test_default_compact(self, hatch, temp_dir, config_file, helpers): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() project = Project(project_path) config = dict(project.raw_config) - config['tool']['hatch']['envs'] = { - 'hatch-test': { - 'matrix': [{'python': ['3.12', '3.10', '3.8']}], - 'dependencies': ['foo', 'bar'], - 'scripts': { - 'run': 'test {env_name}', - 'run-cov': 'test with coverage', - 'cov-combine': 'combine coverage', - 'cov-report': 'show coverage', + config["tool"]["hatch"]["envs"] = { + "hatch-test": { + "matrix": [{"python": ["3.12", "3.10", "3.8"]}], + "dependencies": ["foo", "bar"], + "scripts": { + "run": "test {env_name}", + "run-cov": "test with coverage", + "cov-combine": "combine coverage", + "cov-report": "show coverage", }, } } project.save_config(config) with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('test', '--show') + result = hatch("test", "--show") assert result.exit_code == 0, result.output assert helpers.remove_trailing_spaces(result.output) == helpers.dedent( @@ -1134,39 +1134,39 @@ def test_default_compact(self, hatch, temp_dir, config_file, helpers): ) def test_verbose(self, hatch, temp_dir, config_file, helpers): - config_file.model.template.plugins['default']['tests'] = False + config_file.model.template.plugins["default"]["tests"] = False config_file.save() - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() project = Project(project_path) config = dict(project.raw_config) - config['tool']['hatch']['envs'] = { - 'hatch-test': { - 'matrix': [{'python': ['3.12', '3.10', '3.8']}], - 'dependencies': ['foo', 'bar'], - 'scripts': { - 'run': 'test {env_name}', - 'run-cov': 'test with coverage', - 'cov-combine': 'combine coverage', - 'cov-report': 'show coverage', + config["tool"]["hatch"]["envs"] = { + "hatch-test": { + "matrix": [{"python": ["3.12", "3.10", "3.8"]}], + "dependencies": ["foo", "bar"], + "scripts": { + "run": "test {env_name}", + "run-cov": "test with coverage", + "cov-combine": "combine coverage", + "cov-report": "show coverage", }, - 'overrides': {'matrix': {'python': {'description': {'value': 'test 3.10', 'if': ['3.10']}}}}, + "overrides": {"matrix": {"python": {"description": {"value": "test 3.10", "if": ["3.10"]}}}}, } } project.save_config(config) with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('-v', 'test', '--show') + result = hatch("-v", "test", "--show") assert result.exit_code == 0, result.output assert helpers.remove_trailing_spaces(result.output) == helpers.dedent( diff --git a/tests/cli/test_root.py b/tests/cli/test_root.py index 441c30d15..c7a1efbe2 100644 --- a/tests/cli/test_root.py +++ b/tests/cli/test_root.py @@ -22,15 +22,15 @@ def test_config_file_creation_verbose(self, hatch): with EnvVars(): os.environ.pop(ConfigEnvVars.CONFIG, None) with ConfigFile.get_default_location().temp_hide(): - result = hatch('-v') + result = hatch("-v") assert self.INSTALL_MESSAGE in result.output - result = hatch('-v') + result = hatch("-v") assert self.INSTALL_MESSAGE not in result.output def test_no_subcommand_shows_help(hatch): - assert hatch().output == hatch('--help').output + assert hatch().output == hatch("--help").output def test_no_config_file(hatch, config_file): @@ -38,4 +38,4 @@ def test_no_config_file(hatch, config_file): result = hatch() assert result.exit_code == 1 - assert result.output == f'The selected config file `{config_file.path}` does not exist.\n' + assert result.output == f"The selected config file `{config_file.path}` does not exist.\n" diff --git a/tests/cli/version/test_version.py b/tests/cli/version/test_version.py index d4eb0c419..686d0cc00 100644 --- a/tests/cli/version/test_version.py +++ b/tests/cli/version/test_version.py @@ -10,7 +10,7 @@ class TestNoProject: def test_random_directory(self, hatch, temp_dir, helpers): with temp_dir.as_cwd(): - result = hatch('version') + result = hatch("version") assert result.exit_code == 1, result.output assert result.output == helpers.dedent( @@ -20,14 +20,14 @@ def test_random_directory(self, hatch, temp_dir, helpers): ) def test_configured_project(self, hatch, temp_dir, helpers, config_file): - project = 'foo' - config_file.model.mode = 'project' + project = "foo" + config_file.model.mode = "project" config_file.model.project = project config_file.model.projects = {project: str(temp_dir)} config_file.save() with temp_dir.as_cwd(): - result = hatch('version') + result = hatch("version") assert result.exit_code == 1, result.output assert result.output == helpers.dedent( @@ -38,27 +38,27 @@ def test_configured_project(self, hatch, temp_dir, helpers, config_file): def test_other_backend_show(hatch, temp_dir, helpers): - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() - (path / 'src' / 'my_app' / '__init__.py').write_text('__version__ = "9000.42"') + (path / "src" / "my_app" / "__init__.py").write_text('__version__ = "9000.42"') project = Project(path) config = dict(project.raw_config) - config['build-system']['requires'] = ['flit-core'] - config['build-system']['build-backend'] = 'flit_core.buildapi' - del config['project']['license'] + config["build-system"]["requires"] = ["flit-core"] + config["build-system"]["build-backend"] = "flit_core.buildapi" + del config["project"]["license"] project.save_config(config) with path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('version') + result = hatch("version") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -73,25 +73,25 @@ def test_other_backend_show(hatch, temp_dir, helpers): def test_other_backend_set(hatch, temp_dir, helpers): - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() project = Project(path) config = dict(project.raw_config) - config['build-system']['requires'] = ['flit-core'] - config['build-system']['build-backend'] = 'flit_core.buildapi' - del config['project']['license'] + config["build-system"]["requires"] = ["flit-core"] + config["build-system"]["build-backend"] = "flit_core.buildapi" + del config["project"]["license"] project.save_config(config) with path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('version', '1.0.0') + result = hatch("version", "1.0.0") assert result.exit_code == 1, result.output assert result.output == helpers.dedent( @@ -102,24 +102,24 @@ def test_other_backend_set(hatch, temp_dir, helpers): def test_incompatible_environment(hatch, temp_dir, helpers, build_env_config): - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - result = hatch('new', project_name) + result = hatch("new", project_name) assert result.exit_code == 0, result.output - path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() project = Project(path) config = dict(project.raw_config) - config['build-system']['requires'].append('foo') + config["build-system"]["requires"].append("foo") project.save_config(config) - helpers.update_project_environment(project, 'hatch-build', {'python': '9000', **build_env_config}) + helpers.update_project_environment(project, "hatch-build", {"python": "9000", **build_env_config}) with path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('version') + result = hatch("version") assert result.exit_code == 1, result.output assert result.output == helpers.dedent( @@ -129,19 +129,19 @@ def test_incompatible_environment(hatch, temp_dir, helpers, build_env_config): ) -@pytest.mark.usefixtures('mock_backend_process') +@pytest.mark.usefixtures("mock_backend_process") def test_show_dynamic(hatch, helpers, temp_dir): - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - hatch('new', project_name) + hatch("new", project_name) - path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() with path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('version') + result = hatch("version") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -155,15 +155,15 @@ def test_show_dynamic(hatch, helpers, temp_dir): ) -@pytest.mark.usefixtures('mock_backend_process') +@pytest.mark.usefixtures("mock_backend_process") def test_plugin_dependencies_unmet(hatch, helpers, temp_dir, mock_plugin_installation): - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - hatch('new', project_name) + hatch("new", project_name) - path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() dependency = os.urandom(16).hex() @@ -177,7 +177,7 @@ def test_plugin_dependencies_unmet(hatch, helpers, temp_dir, mock_plugin_install ) with path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('version') + result = hatch("version") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -194,24 +194,24 @@ def test_plugin_dependencies_unmet(hatch, helpers, temp_dir, mock_plugin_install @pytest.mark.requires_internet -@pytest.mark.usefixtures('mock_backend_process') +@pytest.mark.usefixtures("mock_backend_process") def test_no_compatibility_check_if_exists(hatch, helpers, temp_dir, mocker): - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - hatch('new', project_name) + hatch("new", project_name) - project_path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + project_path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() project = Project(project_path) config = dict(project.raw_config) - config['build-system']['requires'].append('binary') + config["build-system"]["requires"].append("binary") project.save_config(config) with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('version') + result = hatch("version") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -224,9 +224,9 @@ def test_no_compatibility_check_if_exists(hatch, helpers, temp_dir, mocker): """ ) - mocker.patch('hatch.env.virtual.VirtualEnvironment.check_compatibility', side_effect=Exception('incompatible')) + mocker.patch("hatch.env.virtual.VirtualEnvironment.check_compatibility", side_effect=Exception("incompatible")) with project_path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('version') + result = hatch("version") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -237,19 +237,19 @@ def test_no_compatibility_check_if_exists(hatch, helpers, temp_dir, mocker): ) -@pytest.mark.usefixtures('mock_backend_process') +@pytest.mark.usefixtures("mock_backend_process") def test_set_dynamic(hatch, helpers, temp_dir): - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - hatch('new', project_name) + hatch("new", project_name) - path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() with path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('version', 'minor,rc') + result = hatch("version", "minor,rc") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -264,7 +264,7 @@ def test_set_dynamic(hatch, helpers, temp_dir): ) with path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('version') + result = hatch("version") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -275,29 +275,29 @@ def test_set_dynamic(hatch, helpers, temp_dir): ) -@pytest.mark.usefixtures('mock_backend_process') +@pytest.mark.usefixtures("mock_backend_process") def test_set_dynamic_downgrade(hatch, helpers, temp_dir): - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - hatch('new', project_name) + hatch("new", project_name) - path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() - (path / 'src' / 'my_app' / '__about__.py').write_text('__version__ = "21.1.2"') + (path / "src" / "my_app" / "__about__.py").write_text('__version__ = "21.1.2"') # This one fails, because it's a downgrade without --force with path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('version', '21.1.0', catch_exceptions=True) + result = hatch("version", "21.1.0", catch_exceptions=True) assert result.exit_code == 1, result.output - assert str(result.exception) == 'Version `21.1.0` is not higher than the original version `21.1.2`' + assert str(result.exception) == "Version `21.1.0` is not higher than the original version `21.1.2`" # Try again, this time with --force with path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('version', '--force', '21.1.0') + result = hatch("version", "--force", "21.1.0") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -309,7 +309,7 @@ def test_set_dynamic_downgrade(hatch, helpers, temp_dir): ) with path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('version') + result = hatch("version") assert result.exit_code == 0, result.output assert result.output == helpers.dedent( @@ -321,45 +321,45 @@ def test_set_dynamic_downgrade(hatch, helpers, temp_dir): def test_show_static(hatch, temp_dir): - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - hatch('new', project_name) + hatch("new", project_name) - path = temp_dir / 'my-app' + path = temp_dir / "my-app" project = Project(path) config = dict(project.raw_config) - config['project']['version'] = '1.2.3' - config['project']['dynamic'].remove('version') - config['tool']['hatch']['metadata'] = {'hooks': {'foo': {}}} + config["project"]["version"] = "1.2.3" + config["project"]["dynamic"].remove("version") + config["tool"]["hatch"]["metadata"] = {"hooks": {"foo": {}}} project.save_config(config) with path.as_cwd(): - result = hatch('version') + result = hatch("version") assert result.exit_code == 0, result.output - assert result.output == '1.2.3\n' + assert result.output == "1.2.3\n" def test_set_static(hatch, helpers, temp_dir): - project_name = 'My.App' + project_name = "My.App" with temp_dir.as_cwd(): - hatch('new', project_name) + hatch("new", project_name) - path = temp_dir / 'my-app' - data_path = temp_dir / 'data' + path = temp_dir / "my-app" + data_path = temp_dir / "data" data_path.mkdir() project = Project(path) config = dict(project.raw_config) - config['project']['version'] = '1.2.3' - config['project']['dynamic'].remove('version') + config["project"]["version"] = "1.2.3" + config["project"]["dynamic"].remove("version") project.save_config(config) with path.as_cwd(env_vars={ConfigEnvVars.DATA: str(data_path)}): - result = hatch('version', 'minor,rc') + result = hatch("version", "minor,rc") assert result.exit_code == 1, result.output assert result.output == helpers.dedent( diff --git a/tests/config/test_model.py b/tests/config/test_model.py index e5f1608d3..449143902 100644 --- a/tests/config/test_model.py +++ b/tests/config/test_model.py @@ -10,33 +10,33 @@ def test_default(default_cache_dir, default_data_dir): config.parse_fields() assert config.raw_data == { - 'mode': 'local', - 'project': '', - 'shell': '', - 'dirs': { - 'project': [], - 'env': {}, - 'python': 'isolated', - 'data': str(default_data_dir), - 'cache': str(default_cache_dir), + "mode": "local", + "project": "", + "shell": "", + "dirs": { + "project": [], + "env": {}, + "python": "isolated", + "data": str(default_data_dir), + "cache": str(default_cache_dir), }, - 'projects': {}, - 'publish': {'index': {'repo': 'main'}}, - 'template': { - 'name': 'Foo Bar', - 'email': 'foo@bar.baz', - 'licenses': {'default': ['MIT'], 'headers': True}, - 'plugins': {'default': {'ci': False, 'src-layout': True, 'tests': True}}, + "projects": {}, + "publish": {"index": {"repo": "main"}}, + "template": { + "name": "Foo Bar", + "email": "foo@bar.baz", + "licenses": {"default": ["MIT"], "headers": True}, + "plugins": {"default": {"ci": False, "src-layout": True, "tests": True}}, }, - 'terminal': { - 'styles': { - 'info': 'bold', - 'success': 'bold cyan', - 'error': 'bold red', - 'warning': 'bold yellow', - 'waiting': 'bold magenta', - 'debug': 'bold', - 'spinner': 'simpleDotsScrolling', + "terminal": { + "styles": { + "info": "bold", + "success": "bold cyan", + "error": "bold red", + "warning": "bold yellow", + "waiting": "bold magenta", + "debug": "bold", + "spinner": "simpleDotsScrolling", }, }, } @@ -46,17 +46,17 @@ class TestMode: def test_default(self): config = RootConfig({}) - assert config.mode == config.mode == 'local' - assert config.raw_data == {'mode': 'local'} + assert config.mode == config.mode == "local" + assert config.raw_data == {"mode": "local"} def test_defined(self): - config = RootConfig({'mode': 'aware'}) + config = RootConfig({"mode": "aware"}) - assert config.mode == 'aware' - assert config.raw_data == {'mode': 'aware'} + assert config.mode == "aware" + assert config.raw_data == {"mode": "aware"} def test_not_string(self, helpers): - config = RootConfig({'mode': 9000}) + config = RootConfig({"mode": 9000}) with pytest.raises( ConfigurationError, @@ -70,7 +70,7 @@ def test_not_string(self, helpers): _ = config.mode def test_unknown(self, helpers): - config = RootConfig({'mode': 'foo'}) + config = RootConfig({"mode": "foo"}) with pytest.raises( ConfigurationError, @@ -87,7 +87,7 @@ def test_set_lazy_error(self, helpers): config = RootConfig({}) config.mode = 9000 - assert config.raw_data == {'mode': 9000} + assert config.raw_data == {"mode": 9000} with pytest.raises( ConfigurationError, @@ -105,17 +105,17 @@ class TestProject: def test_default(self): config = RootConfig({}) - assert config.project == config.project == '' - assert config.raw_data == {'project': ''} + assert config.project == config.project == "" + assert config.raw_data == {"project": ""} def test_defined(self): - config = RootConfig({'project': 'foo'}) + config = RootConfig({"project": "foo"}) - assert config.project == 'foo' - assert config.raw_data == {'project': 'foo'} + assert config.project == "foo" + assert config.raw_data == {"project": "foo"} def test_not_string(self, helpers): - config = RootConfig({'project': 9000}) + config = RootConfig({"project": 9000}) with pytest.raises( ConfigurationError, @@ -132,7 +132,7 @@ def test_set_lazy_error(self, helpers): config = RootConfig({}) config.project = 9000 - assert config.raw_data == {'project': 9000} + assert config.raw_data == {"project": 9000} with pytest.raises( ConfigurationError, @@ -150,13 +150,13 @@ class TestShell: def test_default(self): config = RootConfig({}) - assert config.shell.name == config.shell.name == '' - assert config.shell.path == config.shell.path == '' + assert config.shell.name == config.shell.name == "" + assert config.shell.path == config.shell.path == "" assert config.shell.args == config.shell.args == [] - assert config.raw_data == {'shell': ''} + assert config.raw_data == {"shell": ""} def test_invalid_type(self, helpers): - config = RootConfig({'shell': 9000}) + config = RootConfig({"shell": 9000}) with pytest.raises( ConfigurationError, @@ -170,39 +170,39 @@ def test_invalid_type(self, helpers): _ = config.shell def test_string(self): - config = RootConfig({'shell': 'foo'}) + config = RootConfig({"shell": "foo"}) - assert config.shell.name == 'foo' - assert config.shell.path == 'foo' + assert config.shell.name == "foo" + assert config.shell.path == "foo" assert config.shell.args == [] - assert config.raw_data == {'shell': 'foo'} + assert config.raw_data == {"shell": "foo"} def test_table(self): - config = RootConfig({'shell': {'name': 'foo'}}) + config = RootConfig({"shell": {"name": "foo"}}) - assert config.shell.name == 'foo' - assert config.shell.path == 'foo' + assert config.shell.name == "foo" + assert config.shell.path == "foo" assert config.shell.args == [] - assert config.raw_data == {'shell': {'name': 'foo', 'path': 'foo', 'args': []}} + assert config.raw_data == {"shell": {"name": "foo", "path": "foo", "args": []}} def test_table_with_path(self): - config = RootConfig({'shell': {'name': 'foo', 'path': 'bar'}}) + config = RootConfig({"shell": {"name": "foo", "path": "bar"}}) - assert config.shell.name == 'foo' - assert config.shell.path == 'bar' + assert config.shell.name == "foo" + assert config.shell.path == "bar" assert config.shell.args == [] - assert config.raw_data == {'shell': {'name': 'foo', 'path': 'bar', 'args': []}} + assert config.raw_data == {"shell": {"name": "foo", "path": "bar", "args": []}} def test_table_with_path_and_args(self): - config = RootConfig({'shell': {'name': 'foo', 'path': 'bar', 'args': ['baz']}}) + config = RootConfig({"shell": {"name": "foo", "path": "bar", "args": ["baz"]}}) - assert config.shell.name == 'foo' - assert config.shell.path == 'bar' - assert config.shell.args == ['baz'] - assert config.raw_data == {'shell': {'name': 'foo', 'path': 'bar', 'args': ['baz']}} + assert config.shell.name == "foo" + assert config.shell.path == "bar" + assert config.shell.args == ["baz"] + assert config.raw_data == {"shell": {"name": "foo", "path": "bar", "args": ["baz"]}} def test_table_no_name(self, helpers): - config = RootConfig({'shell': {}}) + config = RootConfig({"shell": {}}) with pytest.raises( ConfigurationError, @@ -216,7 +216,7 @@ def test_table_no_name(self, helpers): _ = config.shell.name def test_table_name_not_string(self, helpers): - config = RootConfig({'shell': {'name': 9000}}) + config = RootConfig({"shell": {"name": 9000}}) with pytest.raises( ConfigurationError, @@ -230,7 +230,7 @@ def test_table_name_not_string(self, helpers): _ = config.shell.name def test_table_path_not_string(self, helpers): - config = RootConfig({'shell': {'path': 9000}}) + config = RootConfig({"shell": {"path": 9000}}) with pytest.raises( ConfigurationError, @@ -244,7 +244,7 @@ def test_table_path_not_string(self, helpers): _ = config.shell.path def test_table_args_not_array(self, helpers): - config = RootConfig({'shell': {'args': 9000}}) + config = RootConfig({"shell": {"args": 9000}}) with pytest.raises( ConfigurationError, @@ -258,7 +258,7 @@ def test_table_args_not_array(self, helpers): _ = config.shell.args def test_table_args_entry_not_string(self, helpers): - config = RootConfig({'shell': {'args': [9000]}}) + config = RootConfig({"shell": {"args": [9000]}}) with pytest.raises( ConfigurationError, @@ -275,7 +275,7 @@ def test_set_lazy_error(self, helpers): config = RootConfig({}) config.shell = 9000 - assert config.raw_data == {'shell': 9000} + assert config.raw_data == {"shell": 9000} with pytest.raises( ConfigurationError, @@ -289,10 +289,10 @@ def test_set_lazy_error(self, helpers): _ = config.shell def test_table_name_set_lazy_error(self, helpers): - config = RootConfig({'shell': {}}) + config = RootConfig({"shell": {}}) config.shell.name = 9000 - assert config.raw_data == {'shell': {'name': 9000}} + assert config.raw_data == {"shell": {"name": 9000}} with pytest.raises( ConfigurationError, @@ -306,10 +306,10 @@ def test_table_name_set_lazy_error(self, helpers): _ = config.shell.name def test_table_path_set_lazy_error(self, helpers): - config = RootConfig({'shell': {'name': 'foo'}}) + config = RootConfig({"shell": {"name": "foo"}}) config.shell.path = 9000 - assert config.raw_data == {'shell': {'name': 'foo', 'path': 9000}} + assert config.raw_data == {"shell": {"name": "foo", "path": 9000}} with pytest.raises( ConfigurationError, @@ -323,10 +323,10 @@ def test_table_path_set_lazy_error(self, helpers): _ = config.shell.path def test_table_args_set_lazy_error(self, helpers): - config = RootConfig({'shell': {'name': 'foo'}}) + config = RootConfig({"shell": {"name": "foo"}}) config.shell.args = 9000 - assert config.raw_data == {'shell': {'name': 'foo', 'args': 9000}} + assert config.raw_data == {"shell": {"name": "foo", "args": 9000}} with pytest.raises( ConfigurationError, @@ -348,21 +348,21 @@ def test_default(self, default_cache_dir, default_data_dir): default_data_directory = str(default_data_dir) assert config.dirs.project == config.dirs.project == [] assert config.dirs.env == config.dirs.env == {} - assert config.dirs.python == config.dirs.python == 'isolated' + assert config.dirs.python == config.dirs.python == "isolated" assert config.dirs.cache == config.dirs.cache == default_cache_directory assert config.dirs.data == config.dirs.data == default_data_directory assert config.raw_data == { - 'dirs': { - 'project': [], - 'env': {}, - 'python': 'isolated', - 'data': default_data_directory, - 'cache': default_cache_directory, + "dirs": { + "project": [], + "env": {}, + "python": "isolated", + "data": default_data_directory, + "cache": default_cache_directory, }, } def test_not_table(self, helpers): - config = RootConfig({'dirs': 9000}) + config = RootConfig({"dirs": 9000}) with pytest.raises( ConfigurationError, @@ -379,7 +379,7 @@ def test_set_lazy_error(self, helpers): config = RootConfig({}) config.dirs = 9000 - assert config.raw_data == {'dirs': 9000} + assert config.raw_data == {"dirs": 9000} with pytest.raises( ConfigurationError, @@ -393,13 +393,13 @@ def test_set_lazy_error(self, helpers): _ = config.dirs def test_project(self): - config = RootConfig({'dirs': {'project': ['foo']}}) + config = RootConfig({"dirs": {"project": ["foo"]}}) - assert config.dirs.project == ['foo'] - assert config.raw_data == {'dirs': {'project': ['foo']}} + assert config.dirs.project == ["foo"] + assert config.raw_data == {"dirs": {"project": ["foo"]}} def test_project_not_array(self, helpers): - config = RootConfig({'dirs': {'project': 9000}}) + config = RootConfig({"dirs": {"project": 9000}}) with pytest.raises( ConfigurationError, @@ -413,7 +413,7 @@ def test_project_not_array(self, helpers): _ = config.dirs.project def test_project_entry_not_string(self, helpers): - config = RootConfig({'dirs': {'project': [9000]}}) + config = RootConfig({"dirs": {"project": [9000]}}) with pytest.raises( ConfigurationError, @@ -430,7 +430,7 @@ def test_project_set_lazy_error(self, helpers): config = RootConfig({}) config.dirs.project = 9000 - assert config.raw_data == {'dirs': {'project': 9000}} + assert config.raw_data == {"dirs": {"project": 9000}} with pytest.raises( ConfigurationError, @@ -444,13 +444,13 @@ def test_project_set_lazy_error(self, helpers): _ = config.dirs.project def test_env(self): - config = RootConfig({'dirs': {'env': {'foo': 'bar'}}}) + config = RootConfig({"dirs": {"env": {"foo": "bar"}}}) - assert config.dirs.env == {'foo': 'bar'} - assert config.raw_data == {'dirs': {'env': {'foo': 'bar'}}} + assert config.dirs.env == {"foo": "bar"} + assert config.raw_data == {"dirs": {"env": {"foo": "bar"}}} def test_env_not_table(self, helpers): - config = RootConfig({'dirs': {'env': 9000}}) + config = RootConfig({"dirs": {"env": 9000}}) with pytest.raises( ConfigurationError, @@ -464,7 +464,7 @@ def test_env_not_table(self, helpers): _ = config.dirs.env def test_env_value_not_string(self, helpers): - config = RootConfig({'dirs': {'env': {'foo': 9000}}}) + config = RootConfig({"dirs": {"env": {"foo": 9000}}}) with pytest.raises( ConfigurationError, @@ -481,7 +481,7 @@ def test_env_set_lazy_error(self, helpers): config = RootConfig({}) config.dirs.env = 9000 - assert config.raw_data == {'dirs': {'env': 9000}} + assert config.raw_data == {"dirs": {"env": 9000}} with pytest.raises( ConfigurationError, @@ -495,13 +495,13 @@ def test_env_set_lazy_error(self, helpers): _ = config.dirs.env def test_python(self): - config = RootConfig({'dirs': {'python': 'foo'}}) + config = RootConfig({"dirs": {"python": "foo"}}) - assert config.dirs.python == 'foo' - assert config.raw_data == {'dirs': {'python': 'foo'}} + assert config.dirs.python == "foo" + assert config.raw_data == {"dirs": {"python": "foo"}} def test_python_not_string(self, helpers): - config = RootConfig({'dirs': {'python': 9000}}) + config = RootConfig({"dirs": {"python": 9000}}) with pytest.raises( ConfigurationError, @@ -518,7 +518,7 @@ def test_python_set_lazy_error(self, helpers): config = RootConfig({}) config.dirs.python = 9000 - assert config.raw_data == {'dirs': {'python': 9000}} + assert config.raw_data == {"dirs": {"python": 9000}} with pytest.raises( ConfigurationError, @@ -532,13 +532,13 @@ def test_python_set_lazy_error(self, helpers): _ = config.dirs.python def test_data(self): - config = RootConfig({'dirs': {'data': 'foo'}}) + config = RootConfig({"dirs": {"data": "foo"}}) - assert config.dirs.data == 'foo' - assert config.raw_data == {'dirs': {'data': 'foo'}} + assert config.dirs.data == "foo" + assert config.raw_data == {"dirs": {"data": "foo"}} def test_data_not_string(self, helpers): - config = RootConfig({'dirs': {'data': 9000}}) + config = RootConfig({"dirs": {"data": 9000}}) with pytest.raises( ConfigurationError, @@ -555,7 +555,7 @@ def test_data_set_lazy_error(self, helpers): config = RootConfig({}) config.dirs.data = 9000 - assert config.raw_data == {'dirs': {'data': 9000}} + assert config.raw_data == {"dirs": {"data": 9000}} with pytest.raises( ConfigurationError, @@ -569,13 +569,13 @@ def test_data_set_lazy_error(self, helpers): _ = config.dirs.data def test_cache(self): - config = RootConfig({'dirs': {'cache': 'foo'}}) + config = RootConfig({"dirs": {"cache": "foo"}}) - assert config.dirs.cache == 'foo' - assert config.raw_data == {'dirs': {'cache': 'foo'}} + assert config.dirs.cache == "foo" + assert config.raw_data == {"dirs": {"cache": "foo"}} def test_cache_not_string(self, helpers): - config = RootConfig({'dirs': {'cache': 9000}}) + config = RootConfig({"dirs": {"cache": 9000}}) with pytest.raises( ConfigurationError, @@ -592,7 +592,7 @@ def test_cache_set_lazy_error(self, helpers): config = RootConfig({}) config.dirs.cache = 9000 - assert config.raw_data == {'dirs': {'cache': 9000}} + assert config.raw_data == {"dirs": {"cache": 9000}} with pytest.raises( ConfigurationError, @@ -611,10 +611,10 @@ def test_default(self): config = RootConfig({}) assert config.projects == config.projects == {} - assert config.raw_data == {'projects': {}} + assert config.raw_data == {"projects": {}} def test_not_table(self, helpers): - config = RootConfig({'projects': 9000}) + config = RootConfig({"projects": 9000}) with pytest.raises( ConfigurationError, @@ -631,7 +631,7 @@ def test_set_lazy_error(self, helpers): config = RootConfig({}) config.projects = 9000 - assert config.raw_data == {'projects': 9000} + assert config.raw_data == {"projects": 9000} with pytest.raises( ConfigurationError, @@ -645,7 +645,7 @@ def test_set_lazy_error(self, helpers): _ = config.projects def test_entry_invalid_type(self, helpers): - config = RootConfig({'projects': {'foo': 9000}}) + config = RootConfig({"projects": {"foo": 9000}}) with pytest.raises( ConfigurationError, @@ -659,21 +659,21 @@ def test_entry_invalid_type(self, helpers): _ = config.projects def test_string(self): - config = RootConfig({'projects': {'foo': 'bar'}}) + config = RootConfig({"projects": {"foo": "bar"}}) - project = config.projects['foo'] - assert project.location == project.location == 'bar' - assert config.raw_data == {'projects': {'foo': 'bar'}} + project = config.projects["foo"] + assert project.location == project.location == "bar" + assert config.raw_data == {"projects": {"foo": "bar"}} def test_table(self): - config = RootConfig({'projects': {'foo': {'location': 'bar'}}}) + config = RootConfig({"projects": {"foo": {"location": "bar"}}}) - project = config.projects['foo'] - assert project.location == project.location == 'bar' - assert config.raw_data == {'projects': {'foo': {'location': 'bar'}}} + project = config.projects["foo"] + assert project.location == project.location == "bar" + assert config.raw_data == {"projects": {"foo": {"location": "bar"}}} def test_table_no_location(self, helpers): - config = RootConfig({'projects': {'foo': {}}}) + config = RootConfig({"projects": {"foo": {}}}) with pytest.raises( ConfigurationError, @@ -684,10 +684,10 @@ def test_table_no_location(self, helpers): required field""" ), ): - _ = config.projects['foo'].location + _ = config.projects["foo"].location def test_location_not_string(self, helpers): - config = RootConfig({'projects': {'foo': {'location': 9000}}}) + config = RootConfig({"projects": {"foo": {"location": 9000}}}) with pytest.raises( ConfigurationError, @@ -698,12 +698,12 @@ def test_location_not_string(self, helpers): must be a string""" ), ): - _ = config.projects['foo'].location + _ = config.projects["foo"].location def test_location_set_lazy_error(self, helpers): - config = RootConfig({'projects': {'foo': {}}}) + config = RootConfig({"projects": {"foo": {}}}) - project = config.projects['foo'] + project = config.projects["foo"] project.location = 9000 with pytest.raises( @@ -722,17 +722,17 @@ class TestPublish: def test_default(self): config = RootConfig({}) - assert config.publish == config.publish == {'index': {'repo': 'main'}} - assert config.raw_data == {'publish': {'index': {'repo': 'main'}}} + assert config.publish == config.publish == {"index": {"repo": "main"}} + assert config.raw_data == {"publish": {"index": {"repo": "main"}}} def test_defined(self): - config = RootConfig({'publish': {'foo': {'username': '', 'password': ''}}}) + config = RootConfig({"publish": {"foo": {"username": "", "password": ""}}}) - assert config.publish == {'foo': {'username': '', 'password': ''}} - assert config.raw_data == {'publish': {'foo': {'username': '', 'password': ''}}} + assert config.publish == {"foo": {"username": "", "password": ""}} + assert config.raw_data == {"publish": {"foo": {"username": "", "password": ""}}} def test_not_table(self, helpers): - config = RootConfig({'publish': 9000}) + config = RootConfig({"publish": 9000}) with pytest.raises( ConfigurationError, @@ -746,7 +746,7 @@ def test_not_table(self, helpers): _ = config.publish def test_data_not_table(self, helpers): - config = RootConfig({'publish': {'foo': 9000}}) + config = RootConfig({"publish": {"foo": 9000}}) with pytest.raises( ConfigurationError, @@ -763,7 +763,7 @@ def test_set_lazy_error(self, helpers): config = RootConfig({}) config.publish = 9000 - assert config.raw_data == {'publish': 9000} + assert config.raw_data == {"publish": 9000} with pytest.raises( ConfigurationError, @@ -779,7 +779,7 @@ def test_set_lazy_error(self, helpers): class TestTemplate: def test_not_table(self, helpers): - config = RootConfig({'template': 9000}) + config = RootConfig({"template": 9000}) with pytest.raises( ConfigurationError, @@ -796,7 +796,7 @@ def test_set_lazy_error(self, helpers): config = RootConfig({}) config.template = 9000 - assert config.raw_data == {'template': 9000} + assert config.raw_data == {"template": 9000} with pytest.raises( ConfigurationError, @@ -810,36 +810,36 @@ def test_set_lazy_error(self, helpers): _ = config.template def test_name(self): - config = RootConfig({'template': {'name': 'foo'}}) + config = RootConfig({"template": {"name": "foo"}}) - assert config.template.name == config.template.name == 'foo' - assert config.raw_data == {'template': {'name': 'foo'}} + assert config.template.name == config.template.name == "foo" + assert config.raw_data == {"template": {"name": "foo"}} def test_name_default_env_var(self): config = RootConfig({}) - assert config.template.name == 'Foo Bar' - assert config.raw_data == {'template': {'name': 'Foo Bar'}} + assert config.template.name == "Foo Bar" + assert config.raw_data == {"template": {"name": "Foo Bar"}} def test_name_default_git(self, temp_dir): config = RootConfig({}) - with temp_dir.as_cwd(exclude=['GIT_AUTHOR_NAME']): - subprocess.check_output(['git', 'init']) - subprocess.check_output(['git', 'config', '--local', 'user.name', 'test']) + with temp_dir.as_cwd(exclude=["GIT_AUTHOR_NAME"]): + subprocess.check_output(["git", "init"]) + subprocess.check_output(["git", "config", "--local", "user.name", "test"]) - assert config.template.name == 'test' - assert config.raw_data == {'template': {'name': 'test'}} + assert config.template.name == "test" + assert config.raw_data == {"template": {"name": "test"}} def test_name_default_no_git(self, temp_dir): config = RootConfig({}) - with temp_dir.as_cwd(exclude=['*']): - assert config.template.name == 'U.N. Owen' - assert config.raw_data == {'template': {'name': 'U.N. Owen'}} + with temp_dir.as_cwd(exclude=["*"]): + assert config.template.name == "U.N. Owen" + assert config.raw_data == {"template": {"name": "U.N. Owen"}} def test_name_not_string(self, helpers): - config = RootConfig({'template': {'name': 9000}}) + config = RootConfig({"template": {"name": 9000}}) with pytest.raises( ConfigurationError, @@ -856,7 +856,7 @@ def test_name_set_lazy_error(self, helpers): config = RootConfig({}) config.template.name = 9000 - assert config.raw_data == {'template': {'name': 9000}} + assert config.raw_data == {"template": {"name": 9000}} with pytest.raises( ConfigurationError, @@ -870,36 +870,36 @@ def test_name_set_lazy_error(self, helpers): _ = config.template.name def test_email(self): - config = RootConfig({'template': {'email': 'foo'}}) + config = RootConfig({"template": {"email": "foo"}}) - assert config.template.email == config.template.email == 'foo' - assert config.raw_data == {'template': {'email': 'foo'}} + assert config.template.email == config.template.email == "foo" + assert config.raw_data == {"template": {"email": "foo"}} def test_email_default_env_var(self): config = RootConfig({}) - assert config.template.email == 'foo@bar.baz' - assert config.raw_data == {'template': {'email': 'foo@bar.baz'}} + assert config.template.email == "foo@bar.baz" + assert config.raw_data == {"template": {"email": "foo@bar.baz"}} def test_email_default_git(self, temp_dir): config = RootConfig({}) - with temp_dir.as_cwd(exclude=['GIT_AUTHOR_EMAIL']): - subprocess.check_output(['git', 'init']) - subprocess.check_output(['git', 'config', '--local', 'user.email', 'test']) + with temp_dir.as_cwd(exclude=["GIT_AUTHOR_EMAIL"]): + subprocess.check_output(["git", "init"]) + subprocess.check_output(["git", "config", "--local", "user.email", "test"]) - assert config.template.email == 'test' - assert config.raw_data == {'template': {'email': 'test'}} + assert config.template.email == "test" + assert config.raw_data == {"template": {"email": "test"}} def test_email_default_no_git(self, temp_dir): config = RootConfig({}) - with temp_dir.as_cwd(exclude=['*']): - assert config.template.email == 'void@some.where' - assert config.raw_data == {'template': {'email': 'void@some.where'}} + with temp_dir.as_cwd(exclude=["*"]): + assert config.template.email == "void@some.where" + assert config.raw_data == {"template": {"email": "void@some.where"}} def test_email_not_string(self, helpers): - config = RootConfig({'template': {'email': 9000}}) + config = RootConfig({"template": {"email": 9000}}) with pytest.raises( ConfigurationError, @@ -916,7 +916,7 @@ def test_email_set_lazy_error(self, helpers): config = RootConfig({}) config.template.email = 9000 - assert config.raw_data == {'template': {'email': 9000}} + assert config.raw_data == {"template": {"email": 9000}} with pytest.raises( ConfigurationError, @@ -930,7 +930,7 @@ def test_email_set_lazy_error(self, helpers): _ = config.template.email def test_licenses_not_table(self, helpers): - config = RootConfig({'template': {'licenses': 9000}}) + config = RootConfig({"template": {"licenses": 9000}}) with pytest.raises( ConfigurationError, @@ -947,7 +947,7 @@ def test_licenses_set_lazy_error(self, helpers): config = RootConfig({}) config.template.licenses = 9000 - assert config.raw_data == {'template': {'licenses': 9000}} + assert config.raw_data == {"template": {"licenses": 9000}} with pytest.raises( ConfigurationError, @@ -961,19 +961,19 @@ def test_licenses_set_lazy_error(self, helpers): _ = config.template.licenses def test_licenses_headers(self): - config = RootConfig({'template': {'licenses': {'headers': False}}}) + config = RootConfig({"template": {"licenses": {"headers": False}}}) assert config.template.licenses.headers is config.template.licenses.headers is False - assert config.raw_data == {'template': {'licenses': {'headers': False}}} + assert config.raw_data == {"template": {"licenses": {"headers": False}}} def test_licenses_headers_default(self): config = RootConfig({}) assert config.template.licenses.headers is config.template.licenses.headers is True - assert config.raw_data == {'template': {'licenses': {'headers': True}}} + assert config.raw_data == {"template": {"licenses": {"headers": True}}} def test_licenses_headers_not_boolean(self, helpers): - config = RootConfig({'template': {'licenses': {'headers': 9000}}}) + config = RootConfig({"template": {"licenses": {"headers": 9000}}}) with pytest.raises( ConfigurationError, @@ -990,7 +990,7 @@ def test_licenses_headers_set_lazy_error(self, helpers): config = RootConfig({}) config.template.licenses.headers = 9000 - assert config.raw_data == {'template': {'licenses': {'headers': 9000}}} + assert config.raw_data == {"template": {"licenses": {"headers": 9000}}} with pytest.raises( ConfigurationError, @@ -1004,19 +1004,19 @@ def test_licenses_headers_set_lazy_error(self, helpers): _ = config.template.licenses.headers def test_licenses_default(self): - config = RootConfig({'template': {'licenses': {'default': ['Apache-2.0', 'MIT']}}}) + config = RootConfig({"template": {"licenses": {"default": ["Apache-2.0", "MIT"]}}}) - assert config.template.licenses.default == config.template.licenses.default == ['Apache-2.0', 'MIT'] - assert config.raw_data == {'template': {'licenses': {'default': ['Apache-2.0', 'MIT']}}} + assert config.template.licenses.default == config.template.licenses.default == ["Apache-2.0", "MIT"] + assert config.raw_data == {"template": {"licenses": {"default": ["Apache-2.0", "MIT"]}}} def test_licenses_default_default(self): config = RootConfig({}) - assert config.template.licenses.default == ['MIT'] - assert config.raw_data == {'template': {'licenses': {'default': ['MIT']}}} + assert config.template.licenses.default == ["MIT"] + assert config.raw_data == {"template": {"licenses": {"default": ["MIT"]}}} def test_licenses_default_not_array(self, helpers): - config = RootConfig({'template': {'licenses': {'default': 9000}}}) + config = RootConfig({"template": {"licenses": {"default": 9000}}}) with pytest.raises( ConfigurationError, @@ -1030,7 +1030,7 @@ def test_licenses_default_not_array(self, helpers): _ = config.template.licenses.default def test_licenses_default_entry_not_string(self, helpers): - config = RootConfig({'template': {'licenses': {'default': [9000]}}}) + config = RootConfig({"template": {"licenses": {"default": [9000]}}}) with pytest.raises( ConfigurationError, @@ -1047,7 +1047,7 @@ def test_licenses_default_set_lazy_error(self, helpers): config = RootConfig({}) config.template.licenses.default = 9000 - assert config.raw_data == {'template': {'licenses': {'default': 9000}}} + assert config.raw_data == {"template": {"licenses": {"default": 9000}}} with pytest.raises( ConfigurationError, @@ -1061,21 +1061,21 @@ def test_licenses_default_set_lazy_error(self, helpers): _ = config.template.licenses.default def test_plugins(self): - config = RootConfig({'template': {'plugins': {'foo': {'bar': 'baz'}}}}) + config = RootConfig({"template": {"plugins": {"foo": {"bar": "baz"}}}}) - assert config.template.plugins == config.template.plugins == {'foo': {'bar': 'baz'}} - assert config.raw_data == {'template': {'plugins': {'foo': {'bar': 'baz'}}}} + assert config.template.plugins == config.template.plugins == {"foo": {"bar": "baz"}} + assert config.raw_data == {"template": {"plugins": {"foo": {"bar": "baz"}}}} def test_plugins_default(self): config = RootConfig({}) - assert config.template.plugins == {'default': {'ci': False, 'src-layout': True, 'tests': True}} + assert config.template.plugins == {"default": {"ci": False, "src-layout": True, "tests": True}} assert config.raw_data == { - 'template': {'plugins': {'default': {'ci': False, 'src-layout': True, 'tests': True}}} + "template": {"plugins": {"default": {"ci": False, "src-layout": True, "tests": True}}} } def test_plugins_not_table(self, helpers): - config = RootConfig({'template': {'plugins': 9000}}) + config = RootConfig({"template": {"plugins": 9000}}) with pytest.raises( ConfigurationError, @@ -1089,7 +1089,7 @@ def test_plugins_not_table(self, helpers): _ = config.template.plugins def test_plugins_data_not_table(self, helpers): - config = RootConfig({'template': {'plugins': {'foo': 9000}}}) + config = RootConfig({"template": {"plugins": {"foo": 9000}}}) with pytest.raises( ConfigurationError, @@ -1106,7 +1106,7 @@ def test_plugins_set_lazy_error(self, helpers): config = RootConfig({}) config.template.plugins = 9000 - assert config.raw_data == {'template': {'plugins': 9000}} + assert config.raw_data == {"template": {"plugins": 9000}} with pytest.raises( ConfigurationError, @@ -1124,29 +1124,29 @@ class TestTerminal: def test_default(self): config = RootConfig({}) - assert config.terminal.styles.info == config.terminal.styles.info == 'bold' - assert config.terminal.styles.success == config.terminal.styles.success == 'bold cyan' - assert config.terminal.styles.error == config.terminal.styles.error == 'bold red' - assert config.terminal.styles.warning == config.terminal.styles.warning == 'bold yellow' - assert config.terminal.styles.waiting == config.terminal.styles.waiting == 'bold magenta' - assert config.terminal.styles.debug == config.terminal.styles.debug == 'bold' - assert config.terminal.styles.spinner == config.terminal.styles.spinner == 'simpleDotsScrolling' + assert config.terminal.styles.info == config.terminal.styles.info == "bold" + assert config.terminal.styles.success == config.terminal.styles.success == "bold cyan" + assert config.terminal.styles.error == config.terminal.styles.error == "bold red" + assert config.terminal.styles.warning == config.terminal.styles.warning == "bold yellow" + assert config.terminal.styles.waiting == config.terminal.styles.waiting == "bold magenta" + assert config.terminal.styles.debug == config.terminal.styles.debug == "bold" + assert config.terminal.styles.spinner == config.terminal.styles.spinner == "simpleDotsScrolling" assert config.raw_data == { - 'terminal': { - 'styles': { - 'info': 'bold', - 'success': 'bold cyan', - 'error': 'bold red', - 'warning': 'bold yellow', - 'waiting': 'bold magenta', - 'debug': 'bold', - 'spinner': 'simpleDotsScrolling', + "terminal": { + "styles": { + "info": "bold", + "success": "bold cyan", + "error": "bold red", + "warning": "bold yellow", + "waiting": "bold magenta", + "debug": "bold", + "spinner": "simpleDotsScrolling", }, }, } def test_not_table(self, helpers): - config = RootConfig({'terminal': 9000}) + config = RootConfig({"terminal": 9000}) with pytest.raises( ConfigurationError, @@ -1163,7 +1163,7 @@ def test_set_lazy_error(self, helpers): config = RootConfig({}) config.terminal = 9000 - assert config.raw_data == {'terminal': 9000} + assert config.raw_data == {"terminal": 9000} with pytest.raises( ConfigurationError, @@ -1177,7 +1177,7 @@ def test_set_lazy_error(self, helpers): _ = config.terminal def test_styles_not_table(self, helpers): - config = RootConfig({'terminal': {'styles': 9000}}) + config = RootConfig({"terminal": {"styles": 9000}}) with pytest.raises( ConfigurationError, @@ -1194,7 +1194,7 @@ def test_styles_set_lazy_error(self, helpers): config = RootConfig({}) config.terminal.styles = 9000 - assert config.raw_data == {'terminal': {'styles': 9000}} + assert config.raw_data == {"terminal": {"styles": 9000}} with pytest.raises( ConfigurationError, @@ -1208,13 +1208,13 @@ def test_styles_set_lazy_error(self, helpers): _ = config.terminal.styles def test_styles_info(self): - config = RootConfig({'terminal': {'styles': {'info': 'foo'}}}) + config = RootConfig({"terminal": {"styles": {"info": "foo"}}}) - assert config.terminal.styles.info == 'foo' - assert config.raw_data == {'terminal': {'styles': {'info': 'foo'}}} + assert config.terminal.styles.info == "foo" + assert config.raw_data == {"terminal": {"styles": {"info": "foo"}}} def test_styles_info_not_string(self, helpers): - config = RootConfig({'terminal': {'styles': {'info': 9000}}}) + config = RootConfig({"terminal": {"styles": {"info": 9000}}}) with pytest.raises( ConfigurationError, @@ -1231,7 +1231,7 @@ def test_styles_info_set_lazy_error(self, helpers): config = RootConfig({}) config.terminal.styles.info = 9000 - assert config.raw_data == {'terminal': {'styles': {'info': 9000}}} + assert config.raw_data == {"terminal": {"styles": {"info": 9000}}} with pytest.raises( ConfigurationError, @@ -1245,13 +1245,13 @@ def test_styles_info_set_lazy_error(self, helpers): _ = config.terminal.styles.info def test_styles_success(self): - config = RootConfig({'terminal': {'styles': {'success': 'foo'}}}) + config = RootConfig({"terminal": {"styles": {"success": "foo"}}}) - assert config.terminal.styles.success == 'foo' - assert config.raw_data == {'terminal': {'styles': {'success': 'foo'}}} + assert config.terminal.styles.success == "foo" + assert config.raw_data == {"terminal": {"styles": {"success": "foo"}}} def test_styles_success_not_string(self, helpers): - config = RootConfig({'terminal': {'styles': {'success': 9000}}}) + config = RootConfig({"terminal": {"styles": {"success": 9000}}}) with pytest.raises( ConfigurationError, @@ -1268,7 +1268,7 @@ def test_styles_success_set_lazy_error(self, helpers): config = RootConfig({}) config.terminal.styles.success = 9000 - assert config.raw_data == {'terminal': {'styles': {'success': 9000}}} + assert config.raw_data == {"terminal": {"styles": {"success": 9000}}} with pytest.raises( ConfigurationError, @@ -1282,13 +1282,13 @@ def test_styles_success_set_lazy_error(self, helpers): _ = config.terminal.styles.success def test_styles_error(self): - config = RootConfig({'terminal': {'styles': {'error': 'foo'}}}) + config = RootConfig({"terminal": {"styles": {"error": "foo"}}}) - assert config.terminal.styles.error == 'foo' - assert config.raw_data == {'terminal': {'styles': {'error': 'foo'}}} + assert config.terminal.styles.error == "foo" + assert config.raw_data == {"terminal": {"styles": {"error": "foo"}}} def test_styles_error_not_string(self, helpers): - config = RootConfig({'terminal': {'styles': {'error': 9000}}}) + config = RootConfig({"terminal": {"styles": {"error": 9000}}}) with pytest.raises( ConfigurationError, @@ -1305,7 +1305,7 @@ def test_styles_error_set_lazy_error(self, helpers): config = RootConfig({}) config.terminal.styles.error = 9000 - assert config.raw_data == {'terminal': {'styles': {'error': 9000}}} + assert config.raw_data == {"terminal": {"styles": {"error": 9000}}} with pytest.raises( ConfigurationError, @@ -1319,13 +1319,13 @@ def test_styles_error_set_lazy_error(self, helpers): _ = config.terminal.styles.error def test_styles_warning(self): - config = RootConfig({'terminal': {'styles': {'warning': 'foo'}}}) + config = RootConfig({"terminal": {"styles": {"warning": "foo"}}}) - assert config.terminal.styles.warning == 'foo' - assert config.raw_data == {'terminal': {'styles': {'warning': 'foo'}}} + assert config.terminal.styles.warning == "foo" + assert config.raw_data == {"terminal": {"styles": {"warning": "foo"}}} def test_styles_warning_not_string(self, helpers): - config = RootConfig({'terminal': {'styles': {'warning': 9000}}}) + config = RootConfig({"terminal": {"styles": {"warning": 9000}}}) with pytest.raises( ConfigurationError, @@ -1342,7 +1342,7 @@ def test_styles_warning_set_lazy_error(self, helpers): config = RootConfig({}) config.terminal.styles.warning = 9000 - assert config.raw_data == {'terminal': {'styles': {'warning': 9000}}} + assert config.raw_data == {"terminal": {"styles": {"warning": 9000}}} with pytest.raises( ConfigurationError, @@ -1356,13 +1356,13 @@ def test_styles_warning_set_lazy_error(self, helpers): _ = config.terminal.styles.warning def test_styles_waiting(self): - config = RootConfig({'terminal': {'styles': {'waiting': 'foo'}}}) + config = RootConfig({"terminal": {"styles": {"waiting": "foo"}}}) - assert config.terminal.styles.waiting == 'foo' - assert config.raw_data == {'terminal': {'styles': {'waiting': 'foo'}}} + assert config.terminal.styles.waiting == "foo" + assert config.raw_data == {"terminal": {"styles": {"waiting": "foo"}}} def test_styles_waiting_not_string(self, helpers): - config = RootConfig({'terminal': {'styles': {'waiting': 9000}}}) + config = RootConfig({"terminal": {"styles": {"waiting": 9000}}}) with pytest.raises( ConfigurationError, @@ -1379,7 +1379,7 @@ def test_styles_waiting_set_lazy_error(self, helpers): config = RootConfig({}) config.terminal.styles.waiting = 9000 - assert config.raw_data == {'terminal': {'styles': {'waiting': 9000}}} + assert config.raw_data == {"terminal": {"styles": {"waiting": 9000}}} with pytest.raises( ConfigurationError, @@ -1393,13 +1393,13 @@ def test_styles_waiting_set_lazy_error(self, helpers): _ = config.terminal.styles.waiting def test_styles_debug(self): - config = RootConfig({'terminal': {'styles': {'debug': 'foo'}}}) + config = RootConfig({"terminal": {"styles": {"debug": "foo"}}}) - assert config.terminal.styles.debug == 'foo' - assert config.raw_data == {'terminal': {'styles': {'debug': 'foo'}}} + assert config.terminal.styles.debug == "foo" + assert config.raw_data == {"terminal": {"styles": {"debug": "foo"}}} def test_styles_debug_not_string(self, helpers): - config = RootConfig({'terminal': {'styles': {'debug': 9000}}}) + config = RootConfig({"terminal": {"styles": {"debug": 9000}}}) with pytest.raises( ConfigurationError, @@ -1416,7 +1416,7 @@ def test_styles_debug_set_lazy_error(self, helpers): config = RootConfig({}) config.terminal.styles.debug = 9000 - assert config.raw_data == {'terminal': {'styles': {'debug': 9000}}} + assert config.raw_data == {"terminal": {"styles": {"debug": 9000}}} with pytest.raises( ConfigurationError, @@ -1430,13 +1430,13 @@ def test_styles_debug_set_lazy_error(self, helpers): _ = config.terminal.styles.debug def test_styles_spinner(self): - config = RootConfig({'terminal': {'styles': {'spinner': 'foo'}}}) + config = RootConfig({"terminal": {"styles": {"spinner": "foo"}}}) - assert config.terminal.styles.spinner == 'foo' - assert config.raw_data == {'terminal': {'styles': {'spinner': 'foo'}}} + assert config.terminal.styles.spinner == "foo" + assert config.raw_data == {"terminal": {"styles": {"spinner": "foo"}}} def test_styles_spinner_not_string(self, helpers): - config = RootConfig({'terminal': {'styles': {'spinner': 9000}}}) + config = RootConfig({"terminal": {"styles": {"spinner": 9000}}}) with pytest.raises( ConfigurationError, @@ -1453,7 +1453,7 @@ def test_styles_spinner_set_lazy_error(self, helpers): config = RootConfig({}) config.terminal.styles.spinner = 9000 - assert config.raw_data == {'terminal': {'styles': {'spinner': 9000}}} + assert config.raw_data == {"terminal": {"styles": {"spinner": 9000}}} with pytest.raises( ConfigurationError, diff --git a/tests/conftest.py b/tests/conftest.py index 370638687..44681a106 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -50,58 +50,58 @@ def __init__(self, command): def __call__(self, *args, **kwargs): # Exceptions should always be handled - kwargs.setdefault('catch_exceptions', False) + kwargs.setdefault("catch_exceptions", False) return self.invoke(self._command, args, **kwargs) -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def hatch(isolation): # noqa: ARG001 from hatch import cli return CliRunner(cli.hatch) -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def helpers(): # https://docs.pytest.org/en/latest/writing_plugins.html#assertion-rewriting - pytest.register_assert_rewrite('tests.helpers.helpers') + pytest.register_assert_rewrite("tests.helpers.helpers") from .helpers import helpers return helpers -@pytest.fixture(scope='session', autouse=True) +@pytest.fixture(scope="session", autouse=True) def isolation(uv_on_path) -> Generator[Path, None, None]: with temp_directory() as d: - data_dir = d / 'data' + data_dir = d / "data" data_dir.mkdir() - cache_dir = d / 'cache' + cache_dir = d / "cache" cache_dir.mkdir() - licenses_dir = cache_dir / 'licenses' + licenses_dir = cache_dir / "licenses" licenses_dir.mkdir() - licenses_dir.joinpath('Apache-2.0.txt').write_text(Apache_2_0) - licenses_dir.joinpath('MIT.txt').write_text(MIT) + licenses_dir.joinpath("Apache-2.0.txt").write_text(Apache_2_0) + licenses_dir.joinpath("MIT.txt").write_text(MIT) default_env_vars = { - AppEnvVars.NO_COLOR: '1', + AppEnvVars.NO_COLOR: "1", ConfigEnvVars.DATA: str(data_dir), ConfigEnvVars.CACHE: str(cache_dir), - PublishEnvVars.REPO: 'dev', - 'HATCH_SELF_TESTING': 'true', - get_env_var(plugin_name='virtual', option='uv_path'): uv_on_path, - 'PYAPP_COMMAND_NAME': os.urandom(4).hex(), - 'GIT_AUTHOR_NAME': 'Foo Bar', - 'GIT_AUTHOR_EMAIL': 'foo@bar.baz', - 'COLUMNS': '80', - 'LINES': '24', + PublishEnvVars.REPO: "dev", + "HATCH_SELF_TESTING": "true", + get_env_var(plugin_name="virtual", option="uv_path"): uv_on_path, + "PYAPP_COMMAND_NAME": os.urandom(4).hex(), + "GIT_AUTHOR_NAME": "Foo Bar", + "GIT_AUTHOR_EMAIL": "foo@bar.baz", + "COLUMNS": "80", + "LINES": "24", } if PLATFORM.windows: - default_env_vars['COMSPEC'] = 'cmd.exe' + default_env_vars["COMSPEC"] = "cmd.exe" else: - default_env_vars['SHELL'] = 'sh' + default_env_vars["SHELL"] = "sh" with d.as_cwd(default_env_vars): os.environ.pop(AppEnvVars.ENV_ACTIVE, None) @@ -109,41 +109,41 @@ def isolation(uv_on_path) -> Generator[Path, None, None]: yield d -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def isolated_data_dir() -> Path: return Path(os.environ[ConfigEnvVars.DATA]) -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def default_data_dir() -> Path: - return Path(user_data_dir('hatch', appauthor=False)) + return Path(user_data_dir("hatch", appauthor=False)) -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def default_cache_dir() -> Path: - return Path(user_cache_dir('hatch', appauthor=False)) + return Path(user_cache_dir("hatch", appauthor=False)) -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def platform(): return PLATFORM -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def current_platform(): return PLATFORM.name -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def current_arch(): import platform return platform.machine().lower() -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def uri_slash_prefix(): - return '//' if os.sep == '/' else '///' + return "//" if os.sep == "/" else "///" @pytest.fixture @@ -154,7 +154,7 @@ def temp_dir() -> Generator[Path, None, None]: @pytest.fixture def temp_dir_data(temp_dir) -> Generator[Path, None, None]: - data_path = temp_dir / 'data' + data_path = temp_dir / "data" data_path.mkdir() with EnvVars({ConfigEnvVars.DATA: str(data_path)}): @@ -163,7 +163,7 @@ def temp_dir_data(temp_dir) -> Generator[Path, None, None]: @pytest.fixture def temp_dir_cache(temp_dir) -> Generator[Path, None, None]: - cache_path = temp_dir / 'cache' + cache_path = temp_dir / "cache" cache_path.mkdir() with EnvVars({ConfigEnvVars.CACHE: str(cache_path)}): @@ -172,24 +172,24 @@ def temp_dir_cache(temp_dir) -> Generator[Path, None, None]: @pytest.fixture(autouse=True) def config_file(tmp_path) -> ConfigFile: - path = Path(tmp_path, 'config.toml') + path = Path(tmp_path, "config.toml") os.environ[ConfigEnvVars.CONFIG] = str(path) config = ConfigFile(path) config.restore() return config -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def default_virtualenv_installed_requirements(helpers): # PyPy installs extra packages by default with TempVirtualEnv(sys.executable, PLATFORM): - output = PLATFORM.run_command(['pip', 'freeze'], check=True, capture_output=True).stdout.decode('utf-8') + output = PLATFORM.run_command(["pip", "freeze"], check=True, capture_output=True).stdout.decode("utf-8") requirements = helpers.extract_requirements(output.splitlines()) return frozenset(requirements) -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def extract_installed_requirements(helpers, default_virtualenv_installed_requirements): return lambda lines: [ requirement @@ -198,24 +198,24 @@ def extract_installed_requirements(helpers, default_virtualenv_installed_require ] -@pytest.fixture(scope='session', autouse=True) +@pytest.fixture(scope="session", autouse=True) def python_on_path(): return Path(sys.executable).stem -@pytest.fixture(scope='session', autouse=True) +@pytest.fixture(scope="session", autouse=True) def uv_on_path(): - return shutil.which('uv') + return shutil.which("uv") -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def compatible_python_distributions(): from hatch.python.resolve import get_compatible_distributions return tuple(get_compatible_distributions()) -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def global_application(): # This is only required for the EnvironmentInterface constructor and will never be used from hatch.cli.application import Application @@ -233,27 +233,27 @@ def temp_application(): @pytest.fixture def build_env_config(): - return get_internal_env_config()['hatch-build'] + return get_internal_env_config()["hatch-build"] -@pytest.fixture(scope='session') +@pytest.fixture(scope="session") def devpi(tmp_path_factory, worker_id): import platform - if not shutil.which('docker') or ( - running_in_ci() and (not PLATFORM.linux or platform.python_implementation() == 'PyPy') + if not shutil.which("docker") or ( + running_in_ci() and (not PLATFORM.linux or platform.python_implementation() == "PyPy") ): - pytest.skip('Not testing publishing') + pytest.skip("Not testing publishing") # This fixture is affected by https://github.com/pytest-dev/pytest-xdist/issues/271 root_tmp_dir = Path(tmp_path_factory.getbasetemp().parent) - devpi_data_file = root_tmp_dir / 'devpi_data.json' - lock_file = f'{devpi_data_file}.lock' - devpi_started_sessions = root_tmp_dir / 'devpi_started_sessions' - devpi_ended_sessions = root_tmp_dir / 'devpi_ended_sessions' - devpi_data = root_tmp_dir / 'devpi_data' - devpi_docker_data = devpi_data / 'docker' + devpi_data_file = root_tmp_dir / "devpi_data.json" + lock_file = f"{devpi_data_file}.lock" + devpi_started_sessions = root_tmp_dir / "devpi_started_sessions" + devpi_ended_sessions = root_tmp_dir / "devpi_ended_sessions" + devpi_data = root_tmp_dir / "devpi_data" + devpi_docker_data = devpi_data / "docker" with FileLock(lock_file): if devpi_data_file.is_file(): data = json.loads(devpi_data_file.read_text()) @@ -264,42 +264,42 @@ def devpi(tmp_path_factory, worker_id): devpi_ended_sessions.mkdir() devpi_data.mkdir() - shutil.copytree(Path(__file__).resolve().parent / 'index' / 'server', devpi_docker_data) + shutil.copytree(Path(__file__).resolve().parent / "index" / "server", devpi_docker_data) # https://github.com/python-trio/trustme/blob/master/trustme/_cli.py # Generate the CA certificate ca = trustme.CA() - cert = ca.issue_cert('localhost', '127.0.0.1', '::1') + cert = ca.issue_cert("localhost", "127.0.0.1", "::1") # Write the certificate and private key the server should use - server_config_dir = devpi_docker_data / 'nginx' - server_key = str(server_config_dir / 'server.key') - server_cert = str(server_config_dir / 'server.pem') + server_config_dir = devpi_docker_data / "nginx" + server_key = str(server_config_dir / "server.key") + server_cert = str(server_config_dir / "server.pem") cert.private_key_pem.write_to_path(path=server_key) - with open(server_cert, mode='w', encoding='utf-8') as f: + with open(server_cert, mode="w", encoding="utf-8") as f: f.truncate() for blob in cert.cert_chain_pems: blob.write_to_path(path=server_cert, append=True) # Write the certificate the client should trust - client_cert = str(devpi_data / 'client.pem') + client_cert = str(devpi_data / "client.pem") ca.cert_pem.write_to_path(path=client_cert) - data = {'password': os.urandom(16).hex(), 'ca_cert': client_cert} - devpi_data_file.write_atomic(json.dumps(data), 'w', encoding='utf-8') + data = {"password": os.urandom(16).hex(), "ca_cert": client_cert} + devpi_data_file.write_atomic(json.dumps(data), "w", encoding="utf-8") - dp = Devpi('https://localhost:8443/hatch/testing/', 'testing', 'hatch', data['password'], data['ca_cert']) - env_vars = {'DEVPI_INDEX_NAME': dp.index_name, 'DEVPI_USERNAME': dp.user, 'DEVPI_PASSWORD': dp.auth} + dp = Devpi("https://localhost:8443/hatch/testing/", "testing", "hatch", data["password"], data["ca_cert"]) + env_vars = {"DEVPI_INDEX_NAME": dp.index_name, "DEVPI_USERNAME": dp.user, "DEVPI_PASSWORD": dp.auth} - compose_file = str(devpi_docker_data / 'docker-compose.yaml') + compose_file = str(devpi_docker_data / "docker-compose.yaml") with FileLock(lock_file): if not any(devpi_started_sessions.iterdir()): with EnvVars(env_vars): - subprocess.check_call(['docker', 'compose', '-f', compose_file, 'up', '--build', '-d']) + subprocess.check_call(["docker", "compose", "-f", compose_file, "up", "--build", "-d"]) for _ in range(60): - output = subprocess.check_output(['docker', 'logs', 'hatch-devpi']).decode('utf-8') - if f'Serving index {dp.user}/{dp.index_name}' in output: + output = subprocess.check_output(["docker", "logs", "hatch-devpi"]).decode("utf-8") + if f"Serving index {dp.user}/{dp.index_name}" in output: time.sleep(5) break @@ -320,17 +320,17 @@ def devpi(tmp_path_factory, worker_id): shutil.rmtree(devpi_ended_sessions) with EnvVars(env_vars): - subprocess.run(['docker', 'compose', '-f', compose_file, 'down', '-t', '0'], capture_output=True) # noqa: PLW1510 + subprocess.run(["docker", "compose", "-f", compose_file, "down", "-t", "0"], capture_output=True) # noqa: PLW1510 shutil.rmtree(devpi_data) @pytest.fixture def env_run(mocker) -> Generator[MagicMock, None, None]: - run = mocker.patch('subprocess.run', return_value=subprocess.CompletedProcess([], 0, stdout=b'')) - mocker.patch('hatch.env.virtual.VirtualEnvironment.exists', return_value=True) - mocker.patch('hatch.env.virtual.VirtualEnvironment.dependency_hash', return_value='') - mocker.patch('hatch.env.virtual.VirtualEnvironment.command_context') + run = mocker.patch("subprocess.run", return_value=subprocess.CompletedProcess([], 0, stdout=b"")) + mocker.patch("hatch.env.virtual.VirtualEnvironment.exists", return_value=True) + mocker.patch("hatch.env.virtual.VirtualEnvironment.dependency_hash", return_value="") + mocker.patch("hatch.env.virtual.VirtualEnvironment.command_context") return run @@ -338,18 +338,18 @@ def is_hatchling_command(command: list[str] | str) -> bool: if isinstance(command, str): command = command.split() - if command[0] != 'python': + if command[0] != "python": return False - if '-m' not in command: + if "-m" not in command: return False - return command[command.index('-m') + 1] == 'hatchling' + return command[command.index("-m") + 1] == "hatchling" @pytest.fixture def mock_backend_process(request, mocker): - if 'allow_backend_process' in request.keywords: + if "allow_backend_process" in request.keywords: yield False return @@ -381,14 +381,14 @@ def mock_process(command: list[str] | str, **kwargs): return mock_process - mocker.patch('hatch.utils.platform.Platform.run_command', side_effect=mock_process_api(PLATFORM.run_command)) + mocker.patch("hatch.utils.platform.Platform.run_command", side_effect=mock_process_api(PLATFORM.run_command)) yield True @pytest.fixture def mock_backend_process_output(request, mocker): - if 'allow_backend_process' in request.keywords: + if "allow_backend_process" in request.keywords: yield False return @@ -417,15 +417,15 @@ def mock_process(command, **kwargs): else: mock.returncode = 0 - mock.stdout = mock.stderr = ''.join(output_queue).encode('utf-8') + mock.stdout = mock.stderr = "".join(output_queue).encode("utf-8") return mock finally: sys.argv = original_args return mock_process - mocker.patch('subprocess.run', side_effect=mock_process_api(subprocess.run)) - mocker.patch('hatchling.bridge.app._display', side_effect=lambda cmd, **_: output_queue.append(f'{cmd}\n')) + mocker.patch("subprocess.run", side_effect=mock_process_api(subprocess.run)) + mocker.patch("hatchling.bridge.app._display", side_effect=lambda cmd, **_: output_queue.append(f"{cmd}\n")) yield True @@ -437,68 +437,68 @@ def mock_plugin_installation(mocker): def _mock(command, **kwargs): if isinstance(command, list): - if command[:5] == [sys.executable, '-u', '-m', 'pip', 'install']: + if command[:5] == [sys.executable, "-u", "-m", "pip", "install"]: mocked_subprocess_run(command, **kwargs) return mocked_subprocess_run - if command[:3] == [sys.executable, 'self', 'python-path']: + if command[:3] == [sys.executable, "self", "python-path"]: return mocker.MagicMock(returncode=0, stdout=sys.executable.encode()) return subprocess_run(command, **kwargs) # no cov - mocker.patch('subprocess.run', side_effect=_mock) + mocker.patch("subprocess.run", side_effect=_mock) return mocked_subprocess_run def pytest_runtest_setup(item): for marker in item.iter_markers(): - if marker.name == 'requires_internet' and not network_connectivity(): # no cov - pytest.skip('No network connectivity') + if marker.name == "requires_internet" and not network_connectivity(): # no cov + pytest.skip("No network connectivity") - if marker.name == 'requires_ci' and not running_in_ci(): # no cov - pytest.skip('Not running in CI') + if marker.name == "requires_ci" and not running_in_ci(): # no cov + pytest.skip("Not running in CI") - if marker.name == 'requires_windows' and not PLATFORM.windows: - pytest.skip('Not running on Windows') + if marker.name == "requires_windows" and not PLATFORM.windows: + pytest.skip("Not running on Windows") - if marker.name == 'requires_macos' and not PLATFORM.macos: - pytest.skip('Not running on macOS') + if marker.name == "requires_macos" and not PLATFORM.macos: + pytest.skip("Not running on macOS") - if marker.name == 'requires_linux' and not PLATFORM.linux: - pytest.skip('Not running on Linux') + if marker.name == "requires_linux" and not PLATFORM.linux: + pytest.skip("Not running on Linux") - if marker.name == 'requires_unix' and PLATFORM.windows: - pytest.skip('Not running on a Linux-based platform') + if marker.name == "requires_unix" and PLATFORM.windows: + pytest.skip("Not running on a Linux-based platform") - if marker.name == 'requires_git' and not git_available(): # no cov - pytest.skip('Git not present in the environment') + if marker.name == "requires_git" and not git_available(): # no cov + pytest.skip("Git not present in the environment") - if marker.name == 'requires_docker' and not docker_available(): # no cov - pytest.skip('Docker not present in the environment') + if marker.name == "requires_docker" and not docker_available(): # no cov + pytest.skip("Docker not present in the environment") - if marker.name == 'requires_cargo' and not cargo_available(): # no cov - pytest.skip('Cargo not present in the environment') + if marker.name == "requires_cargo" and not cargo_available(): # no cov + pytest.skip("Cargo not present in the environment") def pytest_configure(config): - config.addinivalue_line('markers', 'requires_windows: Tests intended for Windows operating systems') - config.addinivalue_line('markers', 'requires_macos: Tests intended for macOS operating systems') - config.addinivalue_line('markers', 'requires_linux: Tests intended for Linux operating systems') - config.addinivalue_line('markers', 'requires_unix: Tests intended for Linux-based operating systems') - config.addinivalue_line('markers', 'requires_internet: Tests that require access to the internet') - config.addinivalue_line('markers', 'requires_git: Tests that require the git command available in the environment') + config.addinivalue_line("markers", "requires_windows: Tests intended for Windows operating systems") + config.addinivalue_line("markers", "requires_macos: Tests intended for macOS operating systems") + config.addinivalue_line("markers", "requires_linux: Tests intended for Linux operating systems") + config.addinivalue_line("markers", "requires_unix: Tests intended for Linux-based operating systems") + config.addinivalue_line("markers", "requires_internet: Tests that require access to the internet") + config.addinivalue_line("markers", "requires_git: Tests that require the git command available in the environment") config.addinivalue_line( - 'markers', 'requires_docker: Tests that require the docker command available in the environment' + "markers", "requires_docker: Tests that require the docker command available in the environment" ) config.addinivalue_line( - 'markers', 'requires_cargo: Tests that require the cargo command available in the environment' + "markers", "requires_cargo: Tests that require the cargo command available in the environment" ) - config.addinivalue_line('markers', 'allow_backend_process: Force the use of backend communication') + config.addinivalue_line("markers", "allow_backend_process: Force the use of backend communication") - config.getini('norecursedirs').remove('build') # /tests/cli/build - config.getini('norecursedirs').remove('venv') # /tests/venv + config.getini("norecursedirs").remove("build") # /tests/cli/build + config.getini("norecursedirs").remove("venv") # /tests/venv @lru_cache @@ -510,7 +510,7 @@ def network_connectivity(): # no cov with suppress(Exception): # Test availability of DNS first - host = socket.gethostbyname('www.google.com') + host = socket.gethostbyname("www.google.com") # Test connection socket.create_connection((host, 80), 2) return True @@ -523,7 +523,7 @@ def git_available(): # no cov if running_in_ci(): return True - return shutil.which('git') is not None + return shutil.which("git") is not None @lru_cache @@ -531,7 +531,7 @@ def docker_available(): # no cov if running_in_ci(): return True - return shutil.which('docker') is not None + return shutil.which("docker") is not None @lru_cache @@ -539,4 +539,4 @@ def cargo_available(): # no cov if running_in_ci(): return True - return shutil.which('cargo') is not None + return shutil.which("cargo") is not None diff --git a/tests/dep/test_sync.py b/tests/dep/test_sync.py index 692dd5756..cc7e49338 100644 --- a/tests/dep/test_sync.py +++ b/tests/dep/test_sync.py @@ -14,21 +14,21 @@ def test_no_dependencies(platform): def test_dependency_not_found(platform): with TempUVVirtualEnv(sys.executable, platform) as venv: - assert not dependencies_in_sync([Requirement('binary')], venv.sys_path) + assert not dependencies_in_sync([Requirement("binary")], venv.sys_path) @pytest.mark.requires_internet def test_dependency_found(platform, uv_on_path): with TempUVVirtualEnv(sys.executable, platform) as venv: - platform.run_command([uv_on_path, 'pip', 'install', 'binary'], check=True, capture_output=True) - assert dependencies_in_sync([Requirement('binary')], venv.sys_path) + platform.run_command([uv_on_path, "pip", "install", "binary"], check=True, capture_output=True) + assert dependencies_in_sync([Requirement("binary")], venv.sys_path) @pytest.mark.requires_internet def test_version_unmet(platform, uv_on_path): with TempUVVirtualEnv(sys.executable, platform) as venv: - platform.run_command([uv_on_path, 'pip', 'install', 'binary'], check=True, capture_output=True) - assert not dependencies_in_sync([Requirement('binary>9000')], venv.sys_path) + platform.run_command([uv_on_path, "pip", "install", "binary"], check=True, capture_output=True) + assert not dependencies_in_sync([Requirement("binary>9000")], venv.sys_path) def test_marker_met(platform): @@ -44,33 +44,33 @@ def test_marker_unmet(platform): @pytest.mark.requires_internet def test_extra_no_dependencies(platform, uv_on_path): with TempUVVirtualEnv(sys.executable, platform) as venv: - platform.run_command([uv_on_path, 'pip', 'install', 'binary'], check=True, capture_output=True) - assert not dependencies_in_sync([Requirement('binary[foo]')], venv.sys_path) + platform.run_command([uv_on_path, "pip", "install", "binary"], check=True, capture_output=True) + assert not dependencies_in_sync([Requirement("binary[foo]")], venv.sys_path) @pytest.mark.requires_internet def test_unknown_extra(platform, uv_on_path): with TempUVVirtualEnv(sys.executable, platform) as venv: platform.run_command( - [uv_on_path, 'pip', 'install', 'requests[security]==2.25.1'], check=True, capture_output=True + [uv_on_path, "pip", "install", "requests[security]==2.25.1"], check=True, capture_output=True ) - assert not dependencies_in_sync([Requirement('requests[foo]')], venv.sys_path) + assert not dependencies_in_sync([Requirement("requests[foo]")], venv.sys_path) @pytest.mark.requires_internet def test_extra_unmet(platform, uv_on_path): with TempUVVirtualEnv(sys.executable, platform) as venv: - platform.run_command([uv_on_path, 'pip', 'install', 'requests==2.25.1'], check=True, capture_output=True) - assert not dependencies_in_sync([Requirement('requests[security]==2.25.1')], venv.sys_path) + platform.run_command([uv_on_path, "pip", "install", "requests==2.25.1"], check=True, capture_output=True) + assert not dependencies_in_sync([Requirement("requests[security]==2.25.1")], venv.sys_path) @pytest.mark.requires_internet def test_extra_met(platform, uv_on_path): with TempUVVirtualEnv(sys.executable, platform) as venv: platform.run_command( - [uv_on_path, 'pip', 'install', 'requests[security]==2.25.1'], check=True, capture_output=True + [uv_on_path, "pip", "install", "requests[security]==2.25.1"], check=True, capture_output=True ) - assert dependencies_in_sync([Requirement('requests[security]==2.25.1')], venv.sys_path) + assert dependencies_in_sync([Requirement("requests[security]==2.25.1")], venv.sys_path) @pytest.mark.requires_internet @@ -78,9 +78,9 @@ def test_extra_met(platform, uv_on_path): def test_dependency_git_pip(platform): with TempVirtualEnv(sys.executable, platform) as venv: platform.run_command( - ['pip', 'install', 'requests@git+https://github.com/psf/requests'], check=True, capture_output=True + ["pip", "install", "requests@git+https://github.com/psf/requests"], check=True, capture_output=True ) - assert dependencies_in_sync([Requirement('requests@git+https://github.com/psf/requests')], venv.sys_path) + assert dependencies_in_sync([Requirement("requests@git+https://github.com/psf/requests")], venv.sys_path) @pytest.mark.requires_internet @@ -88,11 +88,11 @@ def test_dependency_git_pip(platform): def test_dependency_git_uv(platform, uv_on_path): with TempUVVirtualEnv(sys.executable, platform) as venv: platform.run_command( - [uv_on_path, 'pip', 'install', 'requests@git+https://github.com/psf/requests'], + [uv_on_path, "pip", "install", "requests@git+https://github.com/psf/requests"], check=True, capture_output=True, ) - assert dependencies_in_sync([Requirement('requests@git+https://github.com/psf/requests')], venv.sys_path) + assert dependencies_in_sync([Requirement("requests@git+https://github.com/psf/requests")], venv.sys_path) @pytest.mark.requires_internet @@ -100,9 +100,9 @@ def test_dependency_git_uv(platform, uv_on_path): def test_dependency_git_revision_pip(platform): with TempVirtualEnv(sys.executable, platform) as venv: platform.run_command( - ['pip', 'install', 'requests@git+https://github.com/psf/requests@main'], check=True, capture_output=True + ["pip", "install", "requests@git+https://github.com/psf/requests@main"], check=True, capture_output=True ) - assert dependencies_in_sync([Requirement('requests@git+https://github.com/psf/requests@main')], venv.sys_path) + assert dependencies_in_sync([Requirement("requests@git+https://github.com/psf/requests@main")], venv.sys_path) @pytest.mark.requires_internet @@ -110,11 +110,11 @@ def test_dependency_git_revision_pip(platform): def test_dependency_git_revision_uv(platform, uv_on_path): with TempUVVirtualEnv(sys.executable, platform) as venv: platform.run_command( - [uv_on_path, 'pip', 'install', 'requests@git+https://github.com/psf/requests@main'], + [uv_on_path, "pip", "install", "requests@git+https://github.com/psf/requests@main"], check=True, capture_output=True, ) - assert dependencies_in_sync([Requirement('requests@git+https://github.com/psf/requests@main')], venv.sys_path) + assert dependencies_in_sync([Requirement("requests@git+https://github.com/psf/requests@main")], venv.sys_path) @pytest.mark.requires_internet @@ -124,14 +124,14 @@ def test_dependency_git_commit(platform, uv_on_path): platform.run_command( [ uv_on_path, - 'pip', - 'install', - 'requests@git+https://github.com/psf/requests@7f694b79e114c06fac5ec06019cada5a61e5570f', + "pip", + "install", + "requests@git+https://github.com/psf/requests@7f694b79e114c06fac5ec06019cada5a61e5570f", ], check=True, capture_output=True, ) assert dependencies_in_sync( - [Requirement('requests@git+https://github.com/psf/requests@7f694b79e114c06fac5ec06019cada5a61e5570f')], + [Requirement("requests@git+https://github.com/psf/requests@7f694b79e114c06fac5ec06019cada5a61e5570f")], venv.sys_path, ) diff --git a/tests/env/collectors/test_custom.py b/tests/env/collectors/test_custom.py index 2b53e6f2b..f337ed373 100644 --- a/tests/env/collectors/test_custom.py +++ b/tests/env/collectors/test_custom.py @@ -7,25 +7,25 @@ def test_no_path(isolation): - config = {'path': ''} + config = {"path": ""} with pytest.raises( - ValueError, match='Option `path` for environment collector `custom` must not be empty if defined' + ValueError, match="Option `path` for environment collector `custom` must not be empty if defined" ): CustomEnvironmentCollector(str(isolation), config) def test_path_not_string(isolation): - config = {'path': 3} + config = {"path": 3} - with pytest.raises(TypeError, match='Option `path` for environment collector `custom` must be a string'): + with pytest.raises(TypeError, match="Option `path` for environment collector `custom` must be a string"): CustomEnvironmentCollector(str(isolation), config) def test_nonexistent(isolation): - config = {'path': 'test.py'} + config = {"path": "test.py"} - with pytest.raises(OSError, match='Plugin script does not exist: test.py'): + with pytest.raises(OSError, match="Plugin script does not exist: test.py"): CustomEnvironmentCollector(str(isolation), config) @@ -48,13 +48,13 @@ def foo(self): with temp_dir.as_cwd(): hook = CustomEnvironmentCollector(str(temp_dir), config) - assert hook.foo() == ('custom', str(temp_dir)) + assert hook.foo() == ("custom", str(temp_dir)) def test_explicit_path(temp_dir, helpers): - config = {'path': f'foo/{DEFAULT_CUSTOM_SCRIPT}'} + config = {"path": f"foo/{DEFAULT_CUSTOM_SCRIPT}"} - file_path = temp_dir / 'foo' / DEFAULT_CUSTOM_SCRIPT + file_path = temp_dir / "foo" / DEFAULT_CUSTOM_SCRIPT file_path.ensure_parent_dir_exists() file_path.write_text( helpers.dedent( @@ -71,13 +71,13 @@ def foo(self): with temp_dir.as_cwd(): hook = CustomEnvironmentCollector(str(temp_dir), config) - assert hook.foo() == ('custom', str(temp_dir)) + assert hook.foo() == ("custom", str(temp_dir)) def test_no_subclass(temp_dir, helpers): - config = {'path': f'foo/{DEFAULT_CUSTOM_SCRIPT}'} + config = {"path": f"foo/{DEFAULT_CUSTOM_SCRIPT}"} - file_path = temp_dir / 'foo' / DEFAULT_CUSTOM_SCRIPT + file_path = temp_dir / "foo" / DEFAULT_CUSTOM_SCRIPT file_path.ensure_parent_dir_exists() file_path.write_text( helpers.dedent( @@ -93,10 +93,13 @@ class CustomHook: ) ) - with pytest.raises( - ValueError, - match=re.escape( - f'Unable to find a subclass of `EnvironmentCollectorInterface` in `foo/{DEFAULT_CUSTOM_SCRIPT}`: {temp_dir}' + with ( + pytest.raises( + ValueError, + match=re.escape( + f"Unable to find a subclass of `EnvironmentCollectorInterface` in `foo/{DEFAULT_CUSTOM_SCRIPT}`: {temp_dir}" + ), ), - ), temp_dir.as_cwd(): + temp_dir.as_cwd(), + ): CustomEnvironmentCollector(str(temp_dir), config) diff --git a/tests/env/plugin/test_interface.py b/tests/env/plugin/test_interface.py index 645f5090f..4d5ac7370 100644 --- a/tests/env/plugin/test_interface.py +++ b/tests/env/plugin/test_interface.py @@ -7,7 +7,7 @@ class MockEnvironment(EnvironmentInterface): # no cov - PLUGIN_NAME = 'mock' + PLUGIN_NAME = "mock" def find(self): pass @@ -36,13 +36,13 @@ def sync_dependencies(self): class TestEnvVars: def test_default(self, isolation, isolated_data_dir, platform, global_application): - config = {'project': {'name': 'my_app', 'version': '0.0.1'}} + config = {"project": {"name": "my_app", "version": "0.0.1"}} project = Project(isolation, config=config) environment = MockEnvironment( isolation, project.metadata, - 'default', - project.config.envs['default'], + "default", + project.config.envs["default"], {}, isolated_data_dir, isolated_data_dir, @@ -51,19 +51,19 @@ def test_default(self, isolation, isolated_data_dir, platform, global_applicatio global_application, ) - assert environment.env_vars == environment.env_vars == {AppEnvVars.ENV_ACTIVE: 'default'} + assert environment.env_vars == environment.env_vars == {AppEnvVars.ENV_ACTIVE: "default"} def test_not_table(self, isolation, isolated_data_dir, platform, global_application): config = { - 'project': {'name': 'my_app', 'version': '0.0.1'}, - 'tool': {'hatch': {'envs': {'default': {'env-vars': 9000}}}}, + "project": {"name": "my_app", "version": "0.0.1"}, + "tool": {"hatch": {"envs": {"default": {"env-vars": 9000}}}}, } project = Project(isolation, config=config) environment = MockEnvironment( isolation, project.metadata, - 'default', - project.config.envs['default'], + "default", + project.config.envs["default"], {}, isolated_data_dir, isolated_data_dir, @@ -72,20 +72,20 @@ def test_not_table(self, isolation, isolated_data_dir, platform, global_applicat global_application, ) - with pytest.raises(TypeError, match='Field `tool.hatch.envs.default.env-vars` must be a mapping'): + with pytest.raises(TypeError, match="Field `tool.hatch.envs.default.env-vars` must be a mapping"): _ = environment.env_vars def test_value_not_string(self, isolation, isolated_data_dir, platform, global_application): config = { - 'project': {'name': 'my_app', 'version': '0.0.1'}, - 'tool': {'hatch': {'envs': {'default': {'env-vars': {'foo': 9000}}}}}, + "project": {"name": "my_app", "version": "0.0.1"}, + "tool": {"hatch": {"envs": {"default": {"env-vars": {"foo": 9000}}}}}, } project = Project(isolation, config=config) environment = MockEnvironment( isolation, project.metadata, - 'default', - project.config.envs['default'], + "default", + project.config.envs["default"], {}, isolated_data_dir, isolated_data_dir, @@ -95,21 +95,21 @@ def test_value_not_string(self, isolation, isolated_data_dir, platform, global_a ) with pytest.raises( - TypeError, match='Environment variable `foo` of field `tool.hatch.envs.default.env-vars` must be a string' + TypeError, match="Environment variable `foo` of field `tool.hatch.envs.default.env-vars` must be a string" ): _ = environment.env_vars def test_correct(self, isolation, isolated_data_dir, platform, global_application): config = { - 'project': {'name': 'my_app', 'version': '0.0.1'}, - 'tool': {'hatch': {'envs': {'default': {'env-vars': {'foo': 'bar'}}}}}, + "project": {"name": "my_app", "version": "0.0.1"}, + "tool": {"hatch": {"envs": {"default": {"env-vars": {"foo": "bar"}}}}}, } project = Project(isolation, config=config) environment = MockEnvironment( isolation, project.metadata, - 'default', - project.config.envs['default'], + "default", + project.config.envs["default"], {}, isolated_data_dir, isolated_data_dir, @@ -118,20 +118,20 @@ def test_correct(self, isolation, isolated_data_dir, platform, global_applicatio global_application, ) - assert environment.env_vars == {AppEnvVars.ENV_ACTIVE: 'default', 'foo': 'bar'} + assert environment.env_vars == {AppEnvVars.ENV_ACTIVE: "default", "foo": "bar"} def test_context_formatting(self, isolation, isolated_data_dir, platform, global_application): config = { - 'project': {'name': 'my_app', 'version': '0.0.1'}, - 'tool': {'hatch': {'envs': {'default': {'env-vars': {'foo': '{env:FOOBAZ}-{matrix:bar}'}}}}}, + "project": {"name": "my_app", "version": "0.0.1"}, + "tool": {"hatch": {"envs": {"default": {"env-vars": {"foo": "{env:FOOBAZ}-{matrix:bar}"}}}}}, } project = Project(isolation, config=config) environment = MockEnvironment( isolation, project.metadata, - 'default', - project.config.envs['default'], - {'bar': '42'}, + "default", + project.config.envs["default"], + {"bar": "42"}, isolated_data_dir, isolated_data_dir, platform, @@ -139,19 +139,19 @@ def test_context_formatting(self, isolation, isolated_data_dir, platform, global global_application, ) - with EnvVars({'FOOBAZ': 'baz'}): - assert environment.env_vars == {AppEnvVars.ENV_ACTIVE: 'default', 'foo': 'baz-42'} + with EnvVars({"FOOBAZ": "baz"}): + assert environment.env_vars == {AppEnvVars.ENV_ACTIVE: "default", "foo": "baz-42"} class TestEnvInclude: def test_default(self, isolation, isolated_data_dir, platform, global_application): - config = {'project': {'name': 'my_app', 'version': '0.0.1'}} + config = {"project": {"name": "my_app", "version": "0.0.1"}} project = Project(isolation, config=config) environment = MockEnvironment( isolation, project.metadata, - 'default', - project.config.envs['default'], + "default", + project.config.envs["default"], {}, isolated_data_dir, isolated_data_dir, @@ -164,15 +164,15 @@ def test_default(self, isolation, isolated_data_dir, platform, global_applicatio def test_not_array(self, isolation, isolated_data_dir, platform, global_application): config = { - 'project': {'name': 'my_app', 'version': '0.0.1'}, - 'tool': {'hatch': {'envs': {'default': {'env-include': 9000}}}}, + "project": {"name": "my_app", "version": "0.0.1"}, + "tool": {"hatch": {"envs": {"default": {"env-include": 9000}}}}, } project = Project(isolation, config=config) environment = MockEnvironment( isolation, project.metadata, - 'default', - project.config.envs['default'], + "default", + project.config.envs["default"], {}, isolated_data_dir, isolated_data_dir, @@ -181,20 +181,20 @@ def test_not_array(self, isolation, isolated_data_dir, platform, global_applicat global_application, ) - with pytest.raises(TypeError, match='Field `tool.hatch.envs.default.env-include` must be an array'): + with pytest.raises(TypeError, match="Field `tool.hatch.envs.default.env-include` must be an array"): _ = environment.env_include def test_pattern_not_string(self, isolation, isolated_data_dir, platform, global_application): config = { - 'project': {'name': 'my_app', 'version': '0.0.1'}, - 'tool': {'hatch': {'envs': {'default': {'env-include': [9000]}}}}, + "project": {"name": "my_app", "version": "0.0.1"}, + "tool": {"hatch": {"envs": {"default": {"env-include": [9000]}}}}, } project = Project(isolation, config=config) environment = MockEnvironment( isolation, project.metadata, - 'default', - project.config.envs['default'], + "default", + project.config.envs["default"], {}, isolated_data_dir, isolated_data_dir, @@ -204,21 +204,21 @@ def test_pattern_not_string(self, isolation, isolated_data_dir, platform, global ) with pytest.raises( - TypeError, match='Pattern #1 of field `tool.hatch.envs.default.env-include` must be a string' + TypeError, match="Pattern #1 of field `tool.hatch.envs.default.env-include` must be a string" ): _ = environment.env_include def test_correct(self, isolation, isolated_data_dir, platform, global_application): config = { - 'project': {'name': 'my_app', 'version': '0.0.1'}, - 'tool': {'hatch': {'envs': {'default': {'env-include': ['FOO*']}}}}, + "project": {"name": "my_app", "version": "0.0.1"}, + "tool": {"hatch": {"envs": {"default": {"env-include": ["FOO*"]}}}}, } project = Project(isolation, config=config) environment = MockEnvironment( isolation, project.metadata, - 'default', - project.config.envs['default'], + "default", + project.config.envs["default"], {}, isolated_data_dir, isolated_data_dir, @@ -227,18 +227,18 @@ def test_correct(self, isolation, isolated_data_dir, platform, global_applicatio global_application, ) - assert environment.env_include == ['HATCH_BUILD_*', 'FOO*'] + assert environment.env_include == ["HATCH_BUILD_*", "FOO*"] class TestEnvExclude: def test_default(self, isolation, isolated_data_dir, platform, global_application): - config = {'project': {'name': 'my_app', 'version': '0.0.1'}} + config = {"project": {"name": "my_app", "version": "0.0.1"}} project = Project(isolation, config=config) environment = MockEnvironment( isolation, project.metadata, - 'default', - project.config.envs['default'], + "default", + project.config.envs["default"], {}, isolated_data_dir, isolated_data_dir, @@ -251,15 +251,15 @@ def test_default(self, isolation, isolated_data_dir, platform, global_applicatio def test_not_array(self, isolation, isolated_data_dir, platform, global_application): config = { - 'project': {'name': 'my_app', 'version': '0.0.1'}, - 'tool': {'hatch': {'envs': {'default': {'env-exclude': 9000}}}}, + "project": {"name": "my_app", "version": "0.0.1"}, + "tool": {"hatch": {"envs": {"default": {"env-exclude": 9000}}}}, } project = Project(isolation, config=config) environment = MockEnvironment( isolation, project.metadata, - 'default', - project.config.envs['default'], + "default", + project.config.envs["default"], {}, isolated_data_dir, isolated_data_dir, @@ -268,20 +268,20 @@ def test_not_array(self, isolation, isolated_data_dir, platform, global_applicat global_application, ) - with pytest.raises(TypeError, match='Field `tool.hatch.envs.default.env-exclude` must be an array'): + with pytest.raises(TypeError, match="Field `tool.hatch.envs.default.env-exclude` must be an array"): _ = environment.env_exclude def test_pattern_not_string(self, isolation, isolated_data_dir, platform, global_application): config = { - 'project': {'name': 'my_app', 'version': '0.0.1'}, - 'tool': {'hatch': {'envs': {'default': {'env-exclude': [9000]}}}}, + "project": {"name": "my_app", "version": "0.0.1"}, + "tool": {"hatch": {"envs": {"default": {"env-exclude": [9000]}}}}, } project = Project(isolation, config=config) environment = MockEnvironment( isolation, project.metadata, - 'default', - project.config.envs['default'], + "default", + project.config.envs["default"], {}, isolated_data_dir, isolated_data_dir, @@ -291,21 +291,21 @@ def test_pattern_not_string(self, isolation, isolated_data_dir, platform, global ) with pytest.raises( - TypeError, match='Pattern #1 of field `tool.hatch.envs.default.env-exclude` must be a string' + TypeError, match="Pattern #1 of field `tool.hatch.envs.default.env-exclude` must be a string" ): _ = environment.env_exclude def test_correct(self, isolation, isolated_data_dir, platform, global_application): config = { - 'project': {'name': 'my_app', 'version': '0.0.1'}, - 'tool': {'hatch': {'envs': {'default': {'env-exclude': ['FOO*']}}}}, + "project": {"name": "my_app", "version": "0.0.1"}, + "tool": {"hatch": {"envs": {"default": {"env-exclude": ["FOO*"]}}}}, } project = Project(isolation, config=config) environment = MockEnvironment( isolation, project.metadata, - 'default', - project.config.envs['default'], + "default", + project.config.envs["default"], {}, isolated_data_dir, isolated_data_dir, @@ -314,18 +314,18 @@ def test_correct(self, isolation, isolated_data_dir, platform, global_applicatio global_application, ) - assert environment.env_exclude == ['FOO*'] + assert environment.env_exclude == ["FOO*"] class TestPlatforms: def test_default(self, isolation, isolated_data_dir, platform, global_application): - config = {'project': {'name': 'my_app', 'version': '0.0.1'}} + config = {"project": {"name": "my_app", "version": "0.0.1"}} project = Project(isolation, config=config) environment = MockEnvironment( isolation, project.metadata, - 'default', - project.config.envs['default'], + "default", + project.config.envs["default"], {}, isolated_data_dir, isolated_data_dir, @@ -338,15 +338,15 @@ def test_default(self, isolation, isolated_data_dir, platform, global_applicatio def test_not_array(self, isolation, isolated_data_dir, platform, global_application): config = { - 'project': {'name': 'my_app', 'version': '0.0.1'}, - 'tool': {'hatch': {'envs': {'default': {'platforms': 9000}}}}, + "project": {"name": "my_app", "version": "0.0.1"}, + "tool": {"hatch": {"envs": {"default": {"platforms": 9000}}}}, } project = Project(isolation, config=config) environment = MockEnvironment( isolation, project.metadata, - 'default', - project.config.envs['default'], + "default", + project.config.envs["default"], {}, isolated_data_dir, isolated_data_dir, @@ -355,20 +355,20 @@ def test_not_array(self, isolation, isolated_data_dir, platform, global_applicat global_application, ) - with pytest.raises(TypeError, match='Field `tool.hatch.envs.default.platforms` must be an array'): + with pytest.raises(TypeError, match="Field `tool.hatch.envs.default.platforms` must be an array"): _ = environment.platforms def test_entry_not_string(self, isolation, isolated_data_dir, platform, global_application): config = { - 'project': {'name': 'my_app', 'version': '0.0.1'}, - 'tool': {'hatch': {'envs': {'default': {'platforms': [9000]}}}}, + "project": {"name": "my_app", "version": "0.0.1"}, + "tool": {"hatch": {"envs": {"default": {"platforms": [9000]}}}}, } project = Project(isolation, config=config) environment = MockEnvironment( isolation, project.metadata, - 'default', - project.config.envs['default'], + "default", + project.config.envs["default"], {}, isolated_data_dir, isolated_data_dir, @@ -378,21 +378,21 @@ def test_entry_not_string(self, isolation, isolated_data_dir, platform, global_a ) with pytest.raises( - TypeError, match='Platform #1 of field `tool.hatch.envs.default.platforms` must be a string' + TypeError, match="Platform #1 of field `tool.hatch.envs.default.platforms` must be a string" ): _ = environment.platforms def test_correct(self, isolation, isolated_data_dir, platform, global_application): config = { - 'project': {'name': 'my_app', 'version': '0.0.1'}, - 'tool': {'hatch': {'envs': {'default': {'platforms': ['macOS']}}}}, + "project": {"name": "my_app", "version": "0.0.1"}, + "tool": {"hatch": {"envs": {"default": {"platforms": ["macOS"]}}}}, } project = Project(isolation, config=config) environment = MockEnvironment( isolation, project.metadata, - 'default', - project.config.envs['default'], + "default", + project.config.envs["default"], {}, isolated_data_dir, isolated_data_dir, @@ -401,18 +401,18 @@ def test_correct(self, isolation, isolated_data_dir, platform, global_applicatio global_application, ) - assert environment.platforms == ['macos'] + assert environment.platforms == ["macos"] class TestSkipInstall: def test_default_project(self, temp_dir, isolated_data_dir, platform, global_application): - config = {'project': {'name': 'my_app', 'version': '0.0.1'}} + config = {"project": {"name": "my_app", "version": "0.0.1"}} project = Project(temp_dir, config=config) environment = MockEnvironment( temp_dir, project.metadata, - 'default', - project.config.envs['default'], + "default", + project.config.envs["default"], {}, isolated_data_dir, isolated_data_dir, @@ -420,19 +420,19 @@ def test_default_project(self, temp_dir, isolated_data_dir, platform, global_app 0, global_application, ) - (temp_dir / 'pyproject.toml').touch() + (temp_dir / "pyproject.toml").touch() with temp_dir.as_cwd(): assert environment.skip_install is environment.skip_install is False def test_default_no_project(self, isolation, isolated_data_dir, platform, global_application): - config = {'project': {'name': 'my_app', 'version': '0.0.1'}} + config = {"project": {"name": "my_app", "version": "0.0.1"}} project = Project(isolation, config=config) environment = MockEnvironment( isolation, project.metadata, - 'default', - project.config.envs['default'], + "default", + project.config.envs["default"], {}, isolated_data_dir, isolated_data_dir, @@ -445,15 +445,15 @@ def test_default_no_project(self, isolation, isolated_data_dir, platform, global def test_not_boolean(self, isolation, isolated_data_dir, platform, global_application): config = { - 'project': {'name': 'my_app', 'version': '0.0.1'}, - 'tool': {'hatch': {'envs': {'default': {'skip-install': 9000}}}}, + "project": {"name": "my_app", "version": "0.0.1"}, + "tool": {"hatch": {"envs": {"default": {"skip-install": 9000}}}}, } project = Project(isolation, config=config) environment = MockEnvironment( isolation, project.metadata, - 'default', - project.config.envs['default'], + "default", + project.config.envs["default"], {}, isolated_data_dir, isolated_data_dir, @@ -462,20 +462,20 @@ def test_not_boolean(self, isolation, isolated_data_dir, platform, global_applic global_application, ) - with pytest.raises(TypeError, match='Field `tool.hatch.envs.default.skip-install` must be a boolean'): + with pytest.raises(TypeError, match="Field `tool.hatch.envs.default.skip-install` must be a boolean"): _ = environment.skip_install def test_enable(self, isolation, isolated_data_dir, platform, global_application): config = { - 'project': {'name': 'my_app', 'version': '0.0.1'}, - 'tool': {'hatch': {'envs': {'default': {'skip-install': True}}}}, + "project": {"name": "my_app", "version": "0.0.1"}, + "tool": {"hatch": {"envs": {"default": {"skip-install": True}}}}, } project = Project(isolation, config=config) environment = MockEnvironment( isolation, project.metadata, - 'default', - project.config.envs['default'], + "default", + project.config.envs["default"], {}, isolated_data_dir, isolated_data_dir, @@ -489,13 +489,13 @@ def test_enable(self, isolation, isolated_data_dir, platform, global_application class TestDevMode: def test_default(self, isolation, isolated_data_dir, platform, global_application): - config = {'project': {'name': 'my_app', 'version': '0.0.1'}} + config = {"project": {"name": "my_app", "version": "0.0.1"}} project = Project(isolation, config=config) environment = MockEnvironment( isolation, project.metadata, - 'default', - project.config.envs['default'], + "default", + project.config.envs["default"], {}, isolated_data_dir, isolated_data_dir, @@ -508,15 +508,15 @@ def test_default(self, isolation, isolated_data_dir, platform, global_applicatio def test_not_boolean(self, isolation, isolated_data_dir, platform, global_application): config = { - 'project': {'name': 'my_app', 'version': '0.0.1'}, - 'tool': {'hatch': {'envs': {'default': {'dev-mode': 9000}}}}, + "project": {"name": "my_app", "version": "0.0.1"}, + "tool": {"hatch": {"envs": {"default": {"dev-mode": 9000}}}}, } project = Project(isolation, config=config) environment = MockEnvironment( isolation, project.metadata, - 'default', - project.config.envs['default'], + "default", + project.config.envs["default"], {}, isolated_data_dir, isolated_data_dir, @@ -525,20 +525,20 @@ def test_not_boolean(self, isolation, isolated_data_dir, platform, global_applic global_application, ) - with pytest.raises(TypeError, match='Field `tool.hatch.envs.default.dev-mode` must be a boolean'): + with pytest.raises(TypeError, match="Field `tool.hatch.envs.default.dev-mode` must be a boolean"): _ = environment.dev_mode def test_disable(self, isolation, isolated_data_dir, platform, global_application): config = { - 'project': {'name': 'my_app', 'version': '0.0.1'}, - 'tool': {'hatch': {'envs': {'default': {'dev-mode': False}}}}, + "project": {"name": "my_app", "version": "0.0.1"}, + "tool": {"hatch": {"envs": {"default": {"dev-mode": False}}}}, } project = Project(isolation, config=config) environment = MockEnvironment( isolation, project.metadata, - 'default', - project.config.envs['default'], + "default", + project.config.envs["default"], {}, isolated_data_dir, isolated_data_dir, @@ -552,13 +552,13 @@ def test_disable(self, isolation, isolated_data_dir, platform, global_applicatio class TestBuilder: def test_default(self, isolation, isolated_data_dir, platform, global_application): - config = {'project': {'name': 'my_app', 'version': '0.0.1'}} + config = {"project": {"name": "my_app", "version": "0.0.1"}} project = Project(isolation, config=config) environment = MockEnvironment( isolation, project.metadata, - 'default', - project.config.envs['default'], + "default", + project.config.envs["default"], {}, isolated_data_dir, isolated_data_dir, @@ -571,15 +571,15 @@ def test_default(self, isolation, isolated_data_dir, platform, global_applicatio def test_not_boolean(self, isolation, isolated_data_dir, platform, global_application): config = { - 'project': {'name': 'my_app', 'version': '0.0.1'}, - 'tool': {'hatch': {'envs': {'default': {'builder': 9000}}}}, + "project": {"name": "my_app", "version": "0.0.1"}, + "tool": {"hatch": {"envs": {"default": {"builder": 9000}}}}, } project = Project(isolation, config=config) environment = MockEnvironment( isolation, project.metadata, - 'default', - project.config.envs['default'], + "default", + project.config.envs["default"], {}, isolated_data_dir, isolated_data_dir, @@ -588,20 +588,20 @@ def test_not_boolean(self, isolation, isolated_data_dir, platform, global_applic global_application, ) - with pytest.raises(TypeError, match='Field `tool.hatch.envs.default.builder` must be a boolean'): + with pytest.raises(TypeError, match="Field `tool.hatch.envs.default.builder` must be a boolean"): _ = environment.builder def test_enable(self, isolation, isolated_data_dir, platform, global_application): config = { - 'project': {'name': 'my_app', 'version': '0.0.1'}, - 'tool': {'hatch': {'envs': {'default': {'builder': True}}}}, + "project": {"name": "my_app", "version": "0.0.1"}, + "tool": {"hatch": {"envs": {"default": {"builder": True}}}}, } project = Project(isolation, config=config) environment = MockEnvironment( isolation, project.metadata, - 'default', - project.config.envs['default'], + "default", + project.config.envs["default"], {}, isolated_data_dir, isolated_data_dir, @@ -615,13 +615,13 @@ def test_enable(self, isolation, isolated_data_dir, platform, global_application class TestFeatures: def test_default(self, isolation, isolated_data_dir, platform, global_application): - config = {'project': {'name': 'my_app', 'version': '0.0.1'}} + config = {"project": {"name": "my_app", "version": "0.0.1"}} project = Project(isolation, config=config) environment = MockEnvironment( isolation, project.metadata, - 'default', - project.config.envs['default'], + "default", + project.config.envs["default"], {}, isolated_data_dir, isolated_data_dir, @@ -634,15 +634,15 @@ def test_default(self, isolation, isolated_data_dir, platform, global_applicatio def test_invalid_type(self, isolation, isolated_data_dir, platform, global_application): config = { - 'project': {'name': 'my_app', 'version': '0.0.1'}, - 'tool': {'hatch': {'envs': {'default': {'features': 9000}}}}, + "project": {"name": "my_app", "version": "0.0.1"}, + "tool": {"hatch": {"envs": {"default": {"features": 9000}}}}, } project = Project(isolation, config=config) environment = MockEnvironment( isolation, project.metadata, - 'default', - project.config.envs['default'], + "default", + project.config.envs["default"], {}, isolated_data_dir, isolated_data_dir, @@ -651,20 +651,20 @@ def test_invalid_type(self, isolation, isolated_data_dir, platform, global_appli global_application, ) - with pytest.raises(TypeError, match='Field `tool.hatch.envs.default.features` must be an array of strings'): + with pytest.raises(TypeError, match="Field `tool.hatch.envs.default.features` must be an array of strings"): _ = environment.features def test_correct(self, isolation, isolated_data_dir, platform, global_application): config = { - 'project': {'name': 'my_app', 'version': '0.0.1', 'optional-dependencies': {'foo-bar': [], 'baz': []}}, - 'tool': {'hatch': {'envs': {'default': {'features': ['Foo...Bar', 'Baz', 'baZ']}}}}, + "project": {"name": "my_app", "version": "0.0.1", "optional-dependencies": {"foo-bar": [], "baz": []}}, + "tool": {"hatch": {"envs": {"default": {"features": ["Foo...Bar", "Baz", "baZ"]}}}}, } project = Project(isolation, config=config) environment = MockEnvironment( isolation, project.metadata, - 'default', - project.config.envs['default'], + "default", + project.config.envs["default"], {}, isolated_data_dir, isolated_data_dir, @@ -673,19 +673,19 @@ def test_correct(self, isolation, isolated_data_dir, platform, global_applicatio global_application, ) - assert environment.features == ['baz', 'foo-bar'] + assert environment.features == ["baz", "foo-bar"] def test_feature_not_string(self, isolation, isolated_data_dir, platform, global_application): config = { - 'project': {'name': 'my_app', 'version': '0.0.1', 'optional-dependencies': {'foo': [], 'bar': []}}, - 'tool': {'hatch': {'envs': {'default': {'features': [9000]}}}}, + "project": {"name": "my_app", "version": "0.0.1", "optional-dependencies": {"foo": [], "bar": []}}, + "tool": {"hatch": {"envs": {"default": {"features": [9000]}}}}, } project = Project(isolation, config=config) environment = MockEnvironment( isolation, project.metadata, - 'default', - project.config.envs['default'], + "default", + project.config.envs["default"], {}, isolated_data_dir, isolated_data_dir, @@ -694,20 +694,20 @@ def test_feature_not_string(self, isolation, isolated_data_dir, platform, global global_application, ) - with pytest.raises(TypeError, match='Feature #1 of field `tool.hatch.envs.default.features` must be a string'): + with pytest.raises(TypeError, match="Feature #1 of field `tool.hatch.envs.default.features` must be a string"): _ = environment.features def test_feature_empty_string(self, isolation, isolated_data_dir, platform, global_application): config = { - 'project': {'name': 'my_app', 'version': '0.0.1', 'optional-dependencies': {'foo': [], 'bar': []}}, - 'tool': {'hatch': {'envs': {'default': {'features': ['']}}}}, + "project": {"name": "my_app", "version": "0.0.1", "optional-dependencies": {"foo": [], "bar": []}}, + "tool": {"hatch": {"envs": {"default": {"features": [""]}}}}, } project = Project(isolation, config=config) environment = MockEnvironment( isolation, project.metadata, - 'default', - project.config.envs['default'], + "default", + project.config.envs["default"], {}, isolated_data_dir, isolated_data_dir, @@ -717,21 +717,21 @@ def test_feature_empty_string(self, isolation, isolated_data_dir, platform, glob ) with pytest.raises( - ValueError, match='Feature #1 of field `tool.hatch.envs.default.features` cannot be an empty string' + ValueError, match="Feature #1 of field `tool.hatch.envs.default.features` cannot be an empty string" ): _ = environment.features def test_feature_undefined(self, isolation, isolated_data_dir, platform, global_application): config = { - 'project': {'name': 'my_app', 'version': '0.0.1', 'optional-dependencies': {'foo': []}}, - 'tool': {'hatch': {'envs': {'default': {'features': ['foo', 'bar', '']}}}}, + "project": {"name": "my_app", "version": "0.0.1", "optional-dependencies": {"foo": []}}, + "tool": {"hatch": {"envs": {"default": {"features": ["foo", "bar", ""]}}}}, } project = Project(isolation, config=config) environment = MockEnvironment( isolation, project.metadata, - 'default', - project.config.envs['default'], + "default", + project.config.envs["default"], {}, isolated_data_dir, isolated_data_dir, @@ -743,8 +743,8 @@ def test_feature_undefined(self, isolation, isolated_data_dir, platform, global_ with pytest.raises( ValueError, match=( - 'Feature `bar` of field `tool.hatch.envs.default.features` is not defined in ' - 'field `project.optional-dependencies`' + "Feature `bar` of field `tool.hatch.envs.default.features` is not defined in " + "field `project.optional-dependencies`" ), ): _ = environment.features @@ -752,13 +752,13 @@ def test_feature_undefined(self, isolation, isolated_data_dir, platform, global_ class TestDescription: def test_default(self, isolation, isolated_data_dir, platform, global_application): - config = {'project': {'name': 'my_app', 'version': '0.0.1'}} + config = {"project": {"name": "my_app", "version": "0.0.1"}} project = Project(isolation, config=config) environment = MockEnvironment( isolation, project.metadata, - 'default', - project.config.envs['default'], + "default", + project.config.envs["default"], {}, isolated_data_dir, isolated_data_dir, @@ -767,19 +767,19 @@ def test_default(self, isolation, isolated_data_dir, platform, global_applicatio global_application, ) - assert environment.description == environment.description == '' + assert environment.description == environment.description == "" def test_not_string(self, isolation, isolated_data_dir, platform, global_application): config = { - 'project': {'name': 'my_app', 'version': '0.0.1'}, - 'tool': {'hatch': {'envs': {'default': {'description': 9000}}}}, + "project": {"name": "my_app", "version": "0.0.1"}, + "tool": {"hatch": {"envs": {"default": {"description": 9000}}}}, } project = Project(isolation, config=config) environment = MockEnvironment( isolation, project.metadata, - 'default', - project.config.envs['default'], + "default", + project.config.envs["default"], {}, isolated_data_dir, isolated_data_dir, @@ -788,21 +788,21 @@ def test_not_string(self, isolation, isolated_data_dir, platform, global_applica global_application, ) - with pytest.raises(TypeError, match='Field `tool.hatch.envs.default.description` must be a string'): + with pytest.raises(TypeError, match="Field `tool.hatch.envs.default.description` must be a string"): _ = environment.description def test_correct(self, isolation, isolated_data_dir, platform, global_application): - description = 'foo' + description = "foo" config = { - 'project': {'name': 'my_app', 'version': '0.0.1'}, - 'tool': {'hatch': {'envs': {'default': {'description': description}}}}, + "project": {"name": "my_app", "version": "0.0.1"}, + "tool": {"hatch": {"envs": {"default": {"description": description}}}}, } project = Project(isolation, config=config) environment = MockEnvironment( isolation, project.metadata, - 'default', - project.config.envs['default'], + "default", + project.config.envs["default"], {}, isolated_data_dir, isolated_data_dir, @@ -817,8 +817,8 @@ def test_correct(self, isolation, isolated_data_dir, platform, global_applicatio class TestDependencies: def test_default(self, isolation, isolated_data_dir, platform, temp_application): config = { - 'project': {'name': 'my_app', 'version': '0.0.1', 'dependencies': ['dep1']}, - 'tool': {'hatch': {'envs': {'default': {'skip-install': False}}}}, + "project": {"name": "my_app", "version": "0.0.1", "dependencies": ["dep1"]}, + "tool": {"hatch": {"envs": {"default": {"skip-install": False}}}}, } project = Project(isolation, config=config) project.set_app(temp_application) @@ -826,8 +826,8 @@ def test_default(self, isolation, isolated_data_dir, platform, temp_application) environment = MockEnvironment( isolation, project.metadata, - 'default', - project.config.envs['default'], + "default", + project.config.envs["default"], {}, isolated_data_dir, isolated_data_dir, @@ -836,20 +836,20 @@ def test_default(self, isolation, isolated_data_dir, platform, temp_application) temp_application, ) - assert environment.dependencies == environment.dependencies == ['dep1'] + assert environment.dependencies == environment.dependencies == ["dep1"] assert len(environment.dependencies) == len(environment.dependencies_complex) def test_not_array(self, isolation, isolated_data_dir, platform, global_application): config = { - 'project': {'name': 'my_app', 'version': '0.0.1', 'dependencies': ['dep1']}, - 'tool': {'hatch': {'envs': {'default': {'dependencies': 9000}}}}, + "project": {"name": "my_app", "version": "0.0.1", "dependencies": ["dep1"]}, + "tool": {"hatch": {"envs": {"default": {"dependencies": 9000}}}}, } project = Project(isolation, config=config) environment = MockEnvironment( isolation, project.metadata, - 'default', - project.config.envs['default'], + "default", + project.config.envs["default"], {}, isolated_data_dir, isolated_data_dir, @@ -858,20 +858,20 @@ def test_not_array(self, isolation, isolated_data_dir, platform, global_applicat global_application, ) - with pytest.raises(TypeError, match='Field `tool.hatch.envs.default.dependencies` must be an array'): + with pytest.raises(TypeError, match="Field `tool.hatch.envs.default.dependencies` must be an array"): _ = environment.dependencies def test_entry_not_string(self, isolation, isolated_data_dir, platform, global_application): config = { - 'project': {'name': 'my_app', 'version': '0.0.1', 'dependencies': ['dep1']}, - 'tool': {'hatch': {'envs': {'default': {'dependencies': [9000]}}}}, + "project": {"name": "my_app", "version": "0.0.1", "dependencies": ["dep1"]}, + "tool": {"hatch": {"envs": {"default": {"dependencies": [9000]}}}}, } project = Project(isolation, config=config) environment = MockEnvironment( isolation, project.metadata, - 'default', - project.config.envs['default'], + "default", + project.config.envs["default"], {}, isolated_data_dir, isolated_data_dir, @@ -881,21 +881,21 @@ def test_entry_not_string(self, isolation, isolated_data_dir, platform, global_a ) with pytest.raises( - TypeError, match='Dependency #1 of field `tool.hatch.envs.default.dependencies` must be a string' + TypeError, match="Dependency #1 of field `tool.hatch.envs.default.dependencies` must be a string" ): _ = environment.dependencies def test_invalid(self, isolation, isolated_data_dir, platform, global_application): config = { - 'project': {'name': 'my_app', 'version': '0.0.1', 'dependencies': ['dep1']}, - 'tool': {'hatch': {'envs': {'default': {'dependencies': ['foo^1']}}}}, + "project": {"name": "my_app", "version": "0.0.1", "dependencies": ["dep1"]}, + "tool": {"hatch": {"envs": {"default": {"dependencies": ["foo^1"]}}}}, } project = Project(isolation, config=config) environment = MockEnvironment( isolation, project.metadata, - 'default', - project.config.envs['default'], + "default", + project.config.envs["default"], {}, isolated_data_dir, isolated_data_dir, @@ -905,21 +905,21 @@ def test_invalid(self, isolation, isolated_data_dir, platform, global_applicatio ) with pytest.raises( - ValueError, match='Dependency #1 of field `tool.hatch.envs.default.dependencies` is invalid: .+' + ValueError, match="Dependency #1 of field `tool.hatch.envs.default.dependencies` is invalid: .+" ): _ = environment.dependencies def test_extra_not_array(self, isolation, isolated_data_dir, platform, global_application): config = { - 'project': {'name': 'my_app', 'version': '0.0.1', 'dependencies': ['dep1']}, - 'tool': {'hatch': {'envs': {'default': {'extra-dependencies': 9000}}}}, + "project": {"name": "my_app", "version": "0.0.1", "dependencies": ["dep1"]}, + "tool": {"hatch": {"envs": {"default": {"extra-dependencies": 9000}}}}, } project = Project(isolation, config=config) environment = MockEnvironment( isolation, project.metadata, - 'default', - project.config.envs['default'], + "default", + project.config.envs["default"], {}, isolated_data_dir, isolated_data_dir, @@ -928,20 +928,20 @@ def test_extra_not_array(self, isolation, isolated_data_dir, platform, global_ap global_application, ) - with pytest.raises(TypeError, match='Field `tool.hatch.envs.default.extra-dependencies` must be an array'): + with pytest.raises(TypeError, match="Field `tool.hatch.envs.default.extra-dependencies` must be an array"): _ = environment.dependencies def test_extra_entry_not_string(self, isolation, isolated_data_dir, platform, global_application): config = { - 'project': {'name': 'my_app', 'version': '0.0.1', 'dependencies': ['dep1']}, - 'tool': {'hatch': {'envs': {'default': {'extra-dependencies': [9000]}}}}, + "project": {"name": "my_app", "version": "0.0.1", "dependencies": ["dep1"]}, + "tool": {"hatch": {"envs": {"default": {"extra-dependencies": [9000]}}}}, } project = Project(isolation, config=config) environment = MockEnvironment( isolation, project.metadata, - 'default', - project.config.envs['default'], + "default", + project.config.envs["default"], {}, isolated_data_dir, isolated_data_dir, @@ -951,21 +951,21 @@ def test_extra_entry_not_string(self, isolation, isolated_data_dir, platform, gl ) with pytest.raises( - TypeError, match='Dependency #1 of field `tool.hatch.envs.default.extra-dependencies` must be a string' + TypeError, match="Dependency #1 of field `tool.hatch.envs.default.extra-dependencies` must be a string" ): _ = environment.dependencies def test_extra_invalid(self, isolation, isolated_data_dir, platform, global_application): config = { - 'project': {'name': 'my_app', 'version': '0.0.1', 'dependencies': ['dep1']}, - 'tool': {'hatch': {'envs': {'default': {'extra-dependencies': ['foo^1']}}}}, + "project": {"name": "my_app", "version": "0.0.1", "dependencies": ["dep1"]}, + "tool": {"hatch": {"envs": {"default": {"extra-dependencies": ["foo^1"]}}}}, } project = Project(isolation, config=config) environment = MockEnvironment( isolation, project.metadata, - 'default', - project.config.envs['default'], + "default", + project.config.envs["default"], {}, isolated_data_dir, isolated_data_dir, @@ -975,17 +975,17 @@ def test_extra_invalid(self, isolation, isolated_data_dir, platform, global_appl ) with pytest.raises( - ValueError, match='Dependency #1 of field `tool.hatch.envs.default.extra-dependencies` is invalid: .+' + ValueError, match="Dependency #1 of field `tool.hatch.envs.default.extra-dependencies` is invalid: .+" ): _ = environment.dependencies def test_full(self, isolation, isolated_data_dir, platform, temp_application): config = { - 'project': {'name': 'my_app', 'version': '0.0.1', 'dependencies': ['dep1']}, - 'tool': { - 'hatch': { - 'envs': { - 'default': {'skip-install': False, 'dependencies': ['dep2'], 'extra-dependencies': ['dep3']} + "project": {"name": "my_app", "version": "0.0.1", "dependencies": ["dep1"]}, + "tool": { + "hatch": { + "envs": { + "default": {"skip-install": False, "dependencies": ["dep2"], "extra-dependencies": ["dep3"]} } } }, @@ -996,8 +996,8 @@ def test_full(self, isolation, isolated_data_dir, platform, temp_application): environment = MockEnvironment( isolation, project.metadata, - 'default', - project.config.envs['default'], + "default", + project.config.envs["default"], {}, isolated_data_dir, isolated_data_dir, @@ -1006,18 +1006,18 @@ def test_full(self, isolation, isolated_data_dir, platform, temp_application): temp_application, ) - assert environment.dependencies == ['dep2', 'dep3', 'dep1'] + assert environment.dependencies == ["dep2", "dep3", "dep1"] def test_context_formatting(self, isolation, isolated_data_dir, platform, temp_application, uri_slash_prefix): config = { - 'project': {'name': 'my_app', 'version': '0.0.1', 'dependencies': ['dep1']}, - 'tool': { - 'hatch': { - 'envs': { - 'default': { - 'skip-install': False, - 'dependencies': ['dep2'], - 'extra-dependencies': ['proj @ {root:uri}'], + "project": {"name": "my_app", "version": "0.0.1", "dependencies": ["dep1"]}, + "tool": { + "hatch": { + "envs": { + "default": { + "skip-install": False, + "dependencies": ["dep2"], + "extra-dependencies": ["proj @ {root:uri}"], } } } @@ -1029,8 +1029,8 @@ def test_context_formatting(self, isolation, isolated_data_dir, platform, temp_a environment = MockEnvironment( isolation, project.metadata, - 'default', - project.config.envs['default'], + "default", + project.config.envs["default"], {}, isolated_data_dir, isolated_data_dir, @@ -1039,16 +1039,16 @@ def test_context_formatting(self, isolation, isolated_data_dir, platform, temp_a temp_application, ) - normalized_path = str(isolation).replace('\\', '/') - assert environment.dependencies == ['dep2', f'proj@ file:{uri_slash_prefix}{normalized_path}', 'dep1'] + normalized_path = str(isolation).replace("\\", "/") + assert environment.dependencies == ["dep2", f"proj@ file:{uri_slash_prefix}{normalized_path}", "dep1"] def test_full_skip_install(self, isolation, isolated_data_dir, platform, global_application): config = { - 'project': {'name': 'my_app', 'version': '0.0.1', 'dependencies': ['dep1']}, - 'tool': { - 'hatch': { - 'envs': { - 'default': {'dependencies': ['dep2'], 'extra-dependencies': ['dep3'], 'skip-install': True} + "project": {"name": "my_app", "version": "0.0.1", "dependencies": ["dep1"]}, + "tool": { + "hatch": { + "envs": { + "default": {"dependencies": ["dep2"], "extra-dependencies": ["dep3"], "skip-install": True} } } }, @@ -1057,8 +1057,8 @@ def test_full_skip_install(self, isolation, isolated_data_dir, platform, global_ environment = MockEnvironment( isolation, project.metadata, - 'default', - project.config.envs['default'], + "default", + project.config.envs["default"], {}, isolated_data_dir, isolated_data_dir, @@ -1067,24 +1067,24 @@ def test_full_skip_install(self, isolation, isolated_data_dir, platform, global_ global_application, ) - assert environment.dependencies == ['dep2', 'dep3'] + assert environment.dependencies == ["dep2", "dep3"] def test_full_skip_install_and_features(self, isolation, isolated_data_dir, platform, temp_application): config = { - 'project': { - 'name': 'my_app', - 'version': '0.0.1', - 'dependencies': ['dep1'], - 'optional-dependencies': {'feat': ['dep4']}, + "project": { + "name": "my_app", + "version": "0.0.1", + "dependencies": ["dep1"], + "optional-dependencies": {"feat": ["dep4"]}, }, - 'tool': { - 'hatch': { - 'envs': { - 'default': { - 'dependencies': ['dep2'], - 'extra-dependencies': ['dep3'], - 'skip-install': True, - 'features': ['feat'], + "tool": { + "hatch": { + "envs": { + "default": { + "dependencies": ["dep2"], + "extra-dependencies": ["dep3"], + "skip-install": True, + "features": ["feat"], } } } @@ -1096,8 +1096,8 @@ def test_full_skip_install_and_features(self, isolation, isolated_data_dir, plat environment = MockEnvironment( isolation, project.metadata, - 'default', - project.config.envs['default'], + "default", + project.config.envs["default"], {}, isolated_data_dir, isolated_data_dir, @@ -1106,14 +1106,14 @@ def test_full_skip_install_and_features(self, isolation, isolated_data_dir, plat temp_application, ) - assert environment.dependencies == ['dep2', 'dep3', 'dep4'] + assert environment.dependencies == ["dep2", "dep3", "dep4"] def test_full_dev_mode(self, isolation, isolated_data_dir, platform, global_application): config = { - 'project': {'name': 'my_app', 'version': '0.0.1', 'dependencies': ['dep1']}, - 'tool': { - 'hatch': { - 'envs': {'default': {'dependencies': ['dep2'], 'extra-dependencies': ['dep3'], 'dev-mode': False}} + "project": {"name": "my_app", "version": "0.0.1", "dependencies": ["dep1"]}, + "tool": { + "hatch": { + "envs": {"default": {"dependencies": ["dep2"], "extra-dependencies": ["dep3"], "dev-mode": False}} } }, } @@ -1121,8 +1121,8 @@ def test_full_dev_mode(self, isolation, isolated_data_dir, platform, global_appl environment = MockEnvironment( isolation, project.metadata, - 'default', - project.config.envs['default'], + "default", + project.config.envs["default"], {}, isolated_data_dir, isolated_data_dir, @@ -1131,22 +1131,22 @@ def test_full_dev_mode(self, isolation, isolated_data_dir, platform, global_appl global_application, ) - assert environment.dependencies == ['dep2', 'dep3'] + assert environment.dependencies == ["dep2", "dep3"] def test_builder(self, isolation, isolated_data_dir, platform, global_application): config = { - 'build-system': {'requires': ['dep2']}, - 'project': {'name': 'my_app', 'version': '0.0.1', 'dependencies': ['dep1']}, - 'tool': { - 'hatch': {'envs': {'default': {'skip-install': False, 'builder': True, 'dependencies': ['dep3']}}} + "build-system": {"requires": ["dep2"]}, + "project": {"name": "my_app", "version": "0.0.1", "dependencies": ["dep1"]}, + "tool": { + "hatch": {"envs": {"default": {"skip-install": False, "builder": True, "dependencies": ["dep3"]}}} }, } project = Project(isolation, config=config) environment = MockEnvironment( isolation, project.metadata, - 'default', - project.config.envs['default'], + "default", + project.config.envs["default"], {}, isolated_data_dir, isolated_data_dir, @@ -1155,22 +1155,22 @@ def test_builder(self, isolation, isolated_data_dir, platform, global_applicatio global_application, ) - assert environment.dependencies == ['dep3', 'dep2'] + assert environment.dependencies == ["dep3", "dep2"] class TestScripts: - @pytest.mark.parametrize('field', ['scripts', 'extra-scripts']) + @pytest.mark.parametrize("field", ["scripts", "extra-scripts"]) def test_not_table(self, isolation, isolated_data_dir, platform, global_application, field): config = { - 'project': {'name': 'my_app', 'version': '0.0.1'}, - 'tool': {'hatch': {'envs': {'default': {field: 9000}}}}, + "project": {"name": "my_app", "version": "0.0.1"}, + "tool": {"hatch": {"envs": {"default": {field: 9000}}}}, } project = Project(isolation, config=config) environment = MockEnvironment( isolation, project.metadata, - 'default', - project.config.envs['default'], + "default", + project.config.envs["default"], {}, isolated_data_dir, isolated_data_dir, @@ -1179,21 +1179,21 @@ def test_not_table(self, isolation, isolated_data_dir, platform, global_applicat global_application, ) - with pytest.raises(TypeError, match=f'Field `tool.hatch.envs.default.{field}` must be a table'): + with pytest.raises(TypeError, match=f"Field `tool.hatch.envs.default.{field}` must be a table"): _ = environment.scripts - @pytest.mark.parametrize('field', ['scripts', 'extra-scripts']) + @pytest.mark.parametrize("field", ["scripts", "extra-scripts"]) def test_name_contains_spaces(self, isolation, isolated_data_dir, platform, global_application, field): config = { - 'project': {'name': 'my_app', 'version': '0.0.1'}, - 'tool': {'hatch': {'envs': {'default': {field: {'foo bar': []}}}}}, + "project": {"name": "my_app", "version": "0.0.1"}, + "tool": {"hatch": {"envs": {"default": {field: {"foo bar": []}}}}}, } project = Project(isolation, config=config) environment = MockEnvironment( isolation, project.metadata, - 'default', - project.config.envs['default'], + "default", + project.config.envs["default"], {}, isolated_data_dir, isolated_data_dir, @@ -1204,18 +1204,18 @@ def test_name_contains_spaces(self, isolation, isolated_data_dir, platform, glob with pytest.raises( ValueError, - match=f'Script name `foo bar` in field `tool.hatch.envs.default.{field}` must not contain spaces', + match=f"Script name `foo bar` in field `tool.hatch.envs.default.{field}` must not contain spaces", ): _ = environment.scripts def test_default(self, isolation, isolated_data_dir, platform, global_application): - config = {'project': {'name': 'my_app', 'version': '0.0.1'}} + config = {"project": {"name": "my_app", "version": "0.0.1"}} project = Project(isolation, config=config) environment = MockEnvironment( isolation, project.metadata, - 'default', - project.config.envs['default'], + "default", + project.config.envs["default"], {}, isolated_data_dir, isolated_data_dir, @@ -1226,19 +1226,19 @@ def test_default(self, isolation, isolated_data_dir, platform, global_applicatio assert environment.scripts == environment.scripts == {} - @pytest.mark.parametrize('field', ['scripts', 'extra-scripts']) + @pytest.mark.parametrize("field", ["scripts", "extra-scripts"]) def test_single_commands(self, isolation, isolated_data_dir, platform, global_application, field): - script_config = {'foo': 'command1', 'bar': 'command2'} + script_config = {"foo": "command1", "bar": "command2"} config = { - 'project': {'name': 'my_app', 'version': '0.0.1'}, - 'tool': {'hatch': {'envs': {'default': {field: script_config}}}}, + "project": {"name": "my_app", "version": "0.0.1"}, + "tool": {"hatch": {"envs": {"default": {field: script_config}}}}, } project = Project(isolation, config=config) environment = MockEnvironment( isolation, project.metadata, - 'default', - project.config.envs['default'], + "default", + project.config.envs["default"], {}, isolated_data_dir, isolated_data_dir, @@ -1247,21 +1247,21 @@ def test_single_commands(self, isolation, isolated_data_dir, platform, global_ap global_application, ) - assert environment.scripts == {'foo': ['command1'], 'bar': ['command2']} + assert environment.scripts == {"foo": ["command1"], "bar": ["command2"]} - @pytest.mark.parametrize('field', ['scripts', 'extra-scripts']) + @pytest.mark.parametrize("field", ["scripts", "extra-scripts"]) def test_multiple_commands(self, isolation, isolated_data_dir, platform, global_application, field): - script_config = {'foo': 'command1', 'bar': ['command3', 'command2']} + script_config = {"foo": "command1", "bar": ["command3", "command2"]} config = { - 'project': {'name': 'my_app', 'version': '0.0.1'}, - 'tool': {'hatch': {'envs': {'default': {field: script_config}}}}, + "project": {"name": "my_app", "version": "0.0.1"}, + "tool": {"hatch": {"envs": {"default": {field: script_config}}}}, } project = Project(isolation, config=config) environment = MockEnvironment( isolation, project.metadata, - 'default', - project.config.envs['default'], + "default", + project.config.envs["default"], {}, isolated_data_dir, isolated_data_dir, @@ -1270,20 +1270,20 @@ def test_multiple_commands(self, isolation, isolated_data_dir, platform, global_ global_application, ) - assert environment.scripts == {'foo': ['command1'], 'bar': ['command3', 'command2']} + assert environment.scripts == {"foo": ["command1"], "bar": ["command3", "command2"]} - @pytest.mark.parametrize('field', ['scripts', 'extra-scripts']) + @pytest.mark.parametrize("field", ["scripts", "extra-scripts"]) def test_multiple_commands_not_string(self, isolation, isolated_data_dir, platform, global_application, field): config = { - 'project': {'name': 'my_app', 'version': '0.0.1'}, - 'tool': {'hatch': {'envs': {'default': {field: {'foo': [9000]}}}}}, + "project": {"name": "my_app", "version": "0.0.1"}, + "tool": {"hatch": {"envs": {"default": {field: {"foo": [9000]}}}}}, } project = Project(isolation, config=config) environment = MockEnvironment( isolation, project.metadata, - 'default', - project.config.envs['default'], + "default", + project.config.envs["default"], {}, isolated_data_dir, isolated_data_dir, @@ -1293,22 +1293,22 @@ def test_multiple_commands_not_string(self, isolation, isolated_data_dir, platfo ) with pytest.raises( - TypeError, match=f'Command #1 in field `tool.hatch.envs.default.{field}.foo` must be a string' + TypeError, match=f"Command #1 in field `tool.hatch.envs.default.{field}.foo` must be a string" ): _ = environment.scripts - @pytest.mark.parametrize('field', ['scripts', 'extra-scripts']) + @pytest.mark.parametrize("field", ["scripts", "extra-scripts"]) def test_config_invalid_type(self, isolation, isolated_data_dir, platform, global_application, field): config = { - 'project': {'name': 'my_app', 'version': '0.0.1'}, - 'tool': {'hatch': {'envs': {'default': {field: {'foo': 9000}}}}}, + "project": {"name": "my_app", "version": "0.0.1"}, + "tool": {"hatch": {"envs": {"default": {field: {"foo": 9000}}}}}, } project = Project(isolation, config=config) environment = MockEnvironment( isolation, project.metadata, - 'default', - project.config.envs['default'], + "default", + project.config.envs["default"], {}, isolated_data_dir, isolated_data_dir, @@ -1318,23 +1318,23 @@ def test_config_invalid_type(self, isolation, isolated_data_dir, platform, globa ) with pytest.raises( - TypeError, match=f'Field `tool.hatch.envs.default.{field}.foo` must be a string or an array of strings' + TypeError, match=f"Field `tool.hatch.envs.default.{field}.foo` must be a string or an array of strings" ): _ = environment.scripts - @pytest.mark.parametrize('field', ['scripts', 'extra-scripts']) + @pytest.mark.parametrize("field", ["scripts", "extra-scripts"]) def test_command_expansion_basic(self, isolation, isolated_data_dir, platform, global_application, field): - script_config = {'foo': 'command1', 'bar': ['command3', 'foo']} + script_config = {"foo": "command1", "bar": ["command3", "foo"]} config = { - 'project': {'name': 'my_app', 'version': '0.0.1'}, - 'tool': {'hatch': {'envs': {'default': {field: script_config}}}}, + "project": {"name": "my_app", "version": "0.0.1"}, + "tool": {"hatch": {"envs": {"default": {field: script_config}}}}, } project = Project(isolation, config=config) environment = MockEnvironment( isolation, project.metadata, - 'default', - project.config.envs['default'], + "default", + project.config.envs["default"], {}, isolated_data_dir, isolated_data_dir, @@ -1343,25 +1343,25 @@ def test_command_expansion_basic(self, isolation, isolated_data_dir, platform, g global_application, ) - assert environment.scripts == {'foo': ['command1'], 'bar': ['command3', 'command1']} + assert environment.scripts == {"foo": ["command1"], "bar": ["command3", "command1"]} - @pytest.mark.parametrize('field', ['scripts', 'extra-scripts']) + @pytest.mark.parametrize("field", ["scripts", "extra-scripts"]) def test_command_expansion_multiple_nested(self, isolation, isolated_data_dir, platform, global_application, field): script_config = { - 'foo': 'command3', - 'baz': ['command5', 'bar', 'foo', 'command1'], - 'bar': ['command4', 'foo', 'command2'], + "foo": "command3", + "baz": ["command5", "bar", "foo", "command1"], + "bar": ["command4", "foo", "command2"], } config = { - 'project': {'name': 'my_app', 'version': '0.0.1'}, - 'tool': {'hatch': {'envs': {'default': {field: script_config}}}}, + "project": {"name": "my_app", "version": "0.0.1"}, + "tool": {"hatch": {"envs": {"default": {field: script_config}}}}, } project = Project(isolation, config=config) environment = MockEnvironment( isolation, project.metadata, - 'default', - project.config.envs['default'], + "default", + project.config.envs["default"], {}, isolated_data_dir, isolated_data_dir, @@ -1371,30 +1371,30 @@ def test_command_expansion_multiple_nested(self, isolation, isolated_data_dir, p ) assert environment.scripts == { - 'foo': ['command3'], - 'baz': ['command5', 'command4', 'command3', 'command2', 'command3', 'command1'], - 'bar': ['command4', 'command3', 'command2'], + "foo": ["command3"], + "baz": ["command5", "command4", "command3", "command2", "command3", "command1"], + "bar": ["command4", "command3", "command2"], } - @pytest.mark.parametrize('field', ['scripts', 'extra-scripts']) + @pytest.mark.parametrize("field", ["scripts", "extra-scripts"]) def test_command_expansion_multiple_nested_ignore_exit_code( self, isolation, isolated_data_dir, platform, global_application, field ): script_config = { - 'foo': 'command3', - 'baz': ['command5', '- bar', 'foo', 'command1'], - 'bar': ['command4', '- foo', 'command2'], + "foo": "command3", + "baz": ["command5", "- bar", "foo", "command1"], + "bar": ["command4", "- foo", "command2"], } config = { - 'project': {'name': 'my_app', 'version': '0.0.1'}, - 'tool': {'hatch': {'envs': {'default': {field: script_config}}}}, + "project": {"name": "my_app", "version": "0.0.1"}, + "tool": {"hatch": {"envs": {"default": {field: script_config}}}}, } project = Project(isolation, config=config) environment = MockEnvironment( isolation, project.metadata, - 'default', - project.config.envs['default'], + "default", + project.config.envs["default"], {}, isolated_data_dir, isolated_data_dir, @@ -1404,28 +1404,28 @@ def test_command_expansion_multiple_nested_ignore_exit_code( ) assert environment.scripts == { - 'foo': ['command3'], - 'baz': ['command5', '- command4', '- command3', '- command2', 'command3', 'command1'], - 'bar': ['command4', '- command3', 'command2'], + "foo": ["command3"], + "baz": ["command5", "- command4", "- command3", "- command2", "command3", "command1"], + "bar": ["command4", "- command3", "command2"], } - @pytest.mark.parametrize('field', ['scripts', 'extra-scripts']) + @pytest.mark.parametrize("field", ["scripts", "extra-scripts"]) def test_command_expansion_modification(self, isolation, isolated_data_dir, platform, global_application, field): script_config = { - 'foo': 'command3', - 'baz': ['command5', 'bar world', 'foo', 'command1'], - 'bar': ['command4', 'foo hello', 'command2'], + "foo": "command3", + "baz": ["command5", "bar world", "foo", "command1"], + "bar": ["command4", "foo hello", "command2"], } config = { - 'project': {'name': 'my_app', 'version': '0.0.1'}, - 'tool': {'hatch': {'envs': {'default': {field: script_config}}}}, + "project": {"name": "my_app", "version": "0.0.1"}, + "tool": {"hatch": {"envs": {"default": {field: script_config}}}}, } project = Project(isolation, config=config) environment = MockEnvironment( isolation, project.metadata, - 'default', - project.config.envs['default'], + "default", + project.config.envs["default"], {}, isolated_data_dir, isolated_data_dir, @@ -1435,22 +1435,22 @@ def test_command_expansion_modification(self, isolation, isolated_data_dir, plat ) assert environment.scripts == { - 'foo': ['command3'], - 'baz': ['command5', 'command4 world', 'command3 hello world', 'command2 world', 'command3', 'command1'], - 'bar': ['command4', 'command3 hello', 'command2'], + "foo": ["command3"], + "baz": ["command5", "command4 world", "command3 hello world", "command2 world", "command3", "command1"], + "bar": ["command4", "command3 hello", "command2"], } def test_command_expansion_circular_inheritance(self, isolation, isolated_data_dir, platform, global_application): config = { - 'project': {'name': 'my_app', 'version': '0.0.1'}, - 'tool': {'hatch': {'envs': {'default': {'scripts': {'foo': 'bar', 'bar': 'foo'}}}}}, + "project": {"name": "my_app", "version": "0.0.1"}, + "tool": {"hatch": {"envs": {"default": {"scripts": {"foo": "bar", "bar": "foo"}}}}}, } project = Project(isolation, config=config) environment = MockEnvironment( isolation, project.metadata, - 'default', - project.config.envs['default'], + "default", + project.config.envs["default"], {}, isolated_data_dir, isolated_data_dir, @@ -1461,19 +1461,19 @@ def test_command_expansion_circular_inheritance(self, isolation, isolated_data_d with pytest.raises( ValueError, - match='Circular expansion detected for field `tool.hatch.envs.default.scripts`: foo -> bar -> foo', + match="Circular expansion detected for field `tool.hatch.envs.default.scripts`: foo -> bar -> foo", ): _ = environment.scripts def test_extra_less_precedence(self, isolation, isolated_data_dir, platform, global_application): config = { - 'project': {'name': 'my_app', 'version': '0.0.1'}, - 'tool': { - 'hatch': { - 'envs': { - 'default': { - 'extra-scripts': {'foo': 'command4', 'baz': 'command3'}, - 'scripts': {'foo': 'command1', 'bar': 'command2'}, + "project": {"name": "my_app", "version": "0.0.1"}, + "tool": { + "hatch": { + "envs": { + "default": { + "extra-scripts": {"foo": "command4", "baz": "command3"}, + "scripts": {"foo": "command1", "bar": "command2"}, } } }, @@ -1483,8 +1483,8 @@ def test_extra_less_precedence(self, isolation, isolated_data_dir, platform, glo environment = MockEnvironment( isolation, project.metadata, - 'default', - project.config.envs['default'], + "default", + project.config.envs["default"], {}, isolated_data_dir, isolated_data_dir, @@ -1493,18 +1493,18 @@ def test_extra_less_precedence(self, isolation, isolated_data_dir, platform, glo global_application, ) - assert environment.scripts == {'foo': ['command1'], 'bar': ['command2'], 'baz': ['command3']} + assert environment.scripts == {"foo": ["command1"], "bar": ["command2"], "baz": ["command3"]} class TestPreInstallCommands: def test_default(self, isolation, isolated_data_dir, platform, global_application): - config = {'project': {'name': 'my_app', 'version': '0.0.1'}} + config = {"project": {"name": "my_app", "version": "0.0.1"}} project = Project(isolation, config=config) environment = MockEnvironment( isolation, project.metadata, - 'default', - project.config.envs['default'], + "default", + project.config.envs["default"], {}, isolated_data_dir, isolated_data_dir, @@ -1517,15 +1517,15 @@ def test_default(self, isolation, isolated_data_dir, platform, global_applicatio def test_not_array(self, isolation, isolated_data_dir, platform, global_application): config = { - 'project': {'name': 'my_app', 'version': '0.0.1'}, - 'tool': {'hatch': {'envs': {'default': {'pre-install-commands': 9000}}}}, + "project": {"name": "my_app", "version": "0.0.1"}, + "tool": {"hatch": {"envs": {"default": {"pre-install-commands": 9000}}}}, } project = Project(isolation, config=config) environment = MockEnvironment( isolation, project.metadata, - 'default', - project.config.envs['default'], + "default", + project.config.envs["default"], {}, isolated_data_dir, isolated_data_dir, @@ -1534,20 +1534,20 @@ def test_not_array(self, isolation, isolated_data_dir, platform, global_applicat global_application, ) - with pytest.raises(TypeError, match='Field `tool.hatch.envs.default.pre-install-commands` must be an array'): + with pytest.raises(TypeError, match="Field `tool.hatch.envs.default.pre-install-commands` must be an array"): _ = environment.pre_install_commands def test_entry_not_string(self, isolation, isolated_data_dir, platform, global_application): config = { - 'project': {'name': 'my_app', 'version': '0.0.1'}, - 'tool': {'hatch': {'envs': {'default': {'pre-install-commands': [9000]}}}}, + "project": {"name": "my_app", "version": "0.0.1"}, + "tool": {"hatch": {"envs": {"default": {"pre-install-commands": [9000]}}}}, } project = Project(isolation, config=config) environment = MockEnvironment( isolation, project.metadata, - 'default', - project.config.envs['default'], + "default", + project.config.envs["default"], {}, isolated_data_dir, isolated_data_dir, @@ -1557,21 +1557,21 @@ def test_entry_not_string(self, isolation, isolated_data_dir, platform, global_a ) with pytest.raises( - TypeError, match='Command #1 of field `tool.hatch.envs.default.pre-install-commands` must be a string' + TypeError, match="Command #1 of field `tool.hatch.envs.default.pre-install-commands` must be a string" ): _ = environment.pre_install_commands def test_correct(self, isolation, isolated_data_dir, platform, global_application): config = { - 'project': {'name': 'my_app', 'version': '0.0.1'}, - 'tool': {'hatch': {'envs': {'default': {'pre-install-commands': ['baz test']}}}}, + "project": {"name": "my_app", "version": "0.0.1"}, + "tool": {"hatch": {"envs": {"default": {"pre-install-commands": ["baz test"]}}}}, } project = Project(isolation, config=config) environment = MockEnvironment( isolation, project.metadata, - 'default', - project.config.envs['default'], + "default", + project.config.envs["default"], {}, isolated_data_dir, isolated_data_dir, @@ -1580,18 +1580,18 @@ def test_correct(self, isolation, isolated_data_dir, platform, global_applicatio global_application, ) - assert environment.pre_install_commands == ['baz test'] + assert environment.pre_install_commands == ["baz test"] class TestPostInstallCommands: def test_default(self, isolation, isolated_data_dir, platform, global_application): - config = {'project': {'name': 'my_app', 'version': '0.0.1'}} + config = {"project": {"name": "my_app", "version": "0.0.1"}} project = Project(isolation, config=config) environment = MockEnvironment( isolation, project.metadata, - 'default', - project.config.envs['default'], + "default", + project.config.envs["default"], {}, isolated_data_dir, isolated_data_dir, @@ -1604,15 +1604,15 @@ def test_default(self, isolation, isolated_data_dir, platform, global_applicatio def test_not_array(self, isolation, isolated_data_dir, platform, global_application): config = { - 'project': {'name': 'my_app', 'version': '0.0.1'}, - 'tool': {'hatch': {'envs': {'default': {'post-install-commands': 9000}}}}, + "project": {"name": "my_app", "version": "0.0.1"}, + "tool": {"hatch": {"envs": {"default": {"post-install-commands": 9000}}}}, } project = Project(isolation, config=config) environment = MockEnvironment( isolation, project.metadata, - 'default', - project.config.envs['default'], + "default", + project.config.envs["default"], {}, isolated_data_dir, isolated_data_dir, @@ -1621,20 +1621,20 @@ def test_not_array(self, isolation, isolated_data_dir, platform, global_applicat global_application, ) - with pytest.raises(TypeError, match='Field `tool.hatch.envs.default.post-install-commands` must be an array'): + with pytest.raises(TypeError, match="Field `tool.hatch.envs.default.post-install-commands` must be an array"): _ = environment.post_install_commands def test_entry_not_string(self, isolation, isolated_data_dir, platform, global_application): config = { - 'project': {'name': 'my_app', 'version': '0.0.1'}, - 'tool': {'hatch': {'envs': {'default': {'post-install-commands': [9000]}}}}, + "project": {"name": "my_app", "version": "0.0.1"}, + "tool": {"hatch": {"envs": {"default": {"post-install-commands": [9000]}}}}, } project = Project(isolation, config=config) environment = MockEnvironment( isolation, project.metadata, - 'default', - project.config.envs['default'], + "default", + project.config.envs["default"], {}, isolated_data_dir, isolated_data_dir, @@ -1644,21 +1644,21 @@ def test_entry_not_string(self, isolation, isolated_data_dir, platform, global_a ) with pytest.raises( - TypeError, match='Command #1 of field `tool.hatch.envs.default.post-install-commands` must be a string' + TypeError, match="Command #1 of field `tool.hatch.envs.default.post-install-commands` must be a string" ): _ = environment.post_install_commands def test_correct(self, isolation, isolated_data_dir, platform, global_application): config = { - 'project': {'name': 'my_app', 'version': '0.0.1'}, - 'tool': {'hatch': {'envs': {'default': {'post-install-commands': ['baz test']}}}}, + "project": {"name": "my_app", "version": "0.0.1"}, + "tool": {"hatch": {"envs": {"default": {"post-install-commands": ["baz test"]}}}}, } project = Project(isolation, config=config) environment = MockEnvironment( isolation, project.metadata, - 'default', - project.config.envs['default'], + "default", + project.config.envs["default"], {}, isolated_data_dir, isolated_data_dir, @@ -1667,18 +1667,18 @@ def test_correct(self, isolation, isolated_data_dir, platform, global_applicatio global_application, ) - assert environment.post_install_commands == ['baz test'] + assert environment.post_install_commands == ["baz test"] class TestEnvVarOption: def test_unset(self, isolation, isolated_data_dir, platform, global_application): - config = {'project': {'name': 'my_app', 'version': '0.0.1'}} + config = {"project": {"name": "my_app", "version": "0.0.1"}} project = Project(isolation, config=config) environment = MockEnvironment( isolation, project.metadata, - 'default', - project.config.envs['default'], + "default", + project.config.envs["default"], {}, isolated_data_dir, isolated_data_dir, @@ -1687,16 +1687,16 @@ def test_unset(self, isolation, isolated_data_dir, platform, global_application) global_application, ) - assert environment.get_env_var_option('foo') == '' + assert environment.get_env_var_option("foo") == "" def test_set(self, isolation, isolated_data_dir, platform, global_application): - config = {'project': {'name': 'my_app', 'version': '0.0.1'}} + config = {"project": {"name": "my_app", "version": "0.0.1"}} project = Project(isolation, config=config) environment = MockEnvironment( isolation, project.metadata, - 'default', - project.config.envs['default'], + "default", + project.config.envs["default"], {}, isolated_data_dir, isolated_data_dir, @@ -1705,22 +1705,22 @@ def test_set(self, isolation, isolated_data_dir, platform, global_application): global_application, ) - with EnvVars({'HATCH_ENV_TYPE_MOCK_FOO': 'bar'}): - assert environment.get_env_var_option('foo') == 'bar' + with EnvVars({"HATCH_ENV_TYPE_MOCK_FOO": "bar"}): + assert environment.get_env_var_option("foo") == "bar" class TestContextFormatting: def test_env_name(self, isolation, isolated_data_dir, platform, global_application): config = { - 'project': {'name': 'my_app', 'version': '0.0.1'}, - 'tool': {'hatch': {'envs': {'default': {'scripts': {'foo': 'command {env_name}'}}}}}, + "project": {"name": "my_app", "version": "0.0.1"}, + "tool": {"hatch": {"envs": {"default": {"scripts": {"foo": "command {env_name}"}}}}}, } project = Project(isolation, config=config) environment = MockEnvironment( isolation, project.metadata, - 'default', - project.config.envs['default'], + "default", + project.config.envs["default"], {}, isolated_data_dir, isolated_data_dir, @@ -1729,19 +1729,19 @@ def test_env_name(self, isolation, isolated_data_dir, platform, global_applicati global_application, ) - assert list(environment.expand_command('foo')) == ['command default'] + assert list(environment.expand_command("foo")) == ["command default"] def test_env_type(self, isolation, isolated_data_dir, platform, global_application): config = { - 'project': {'name': 'my_app', 'version': '0.0.1'}, - 'tool': {'hatch': {'envs': {'default': {'scripts': {'foo': 'command {env_type}'}}}}}, + "project": {"name": "my_app", "version": "0.0.1"}, + "tool": {"hatch": {"envs": {"default": {"scripts": {"foo": "command {env_type}"}}}}}, } project = Project(isolation, config=config) environment = MockEnvironment( isolation, project.metadata, - 'default', - project.config.envs['default'], + "default", + project.config.envs["default"], {}, isolated_data_dir, isolated_data_dir, @@ -1750,19 +1750,19 @@ def test_env_type(self, isolation, isolated_data_dir, platform, global_applicati global_application, ) - assert list(environment.expand_command('foo')) == ['command mock'] + assert list(environment.expand_command("foo")) == ["command mock"] def test_verbosity_default(self, isolation, isolated_data_dir, platform, global_application): config = { - 'project': {'name': 'my_app', 'version': '0.0.1'}, - 'tool': {'hatch': {'envs': {'default': {'scripts': {'foo': 'command -v={verbosity}'}}}}}, + "project": {"name": "my_app", "version": "0.0.1"}, + "tool": {"hatch": {"envs": {"default": {"scripts": {"foo": "command -v={verbosity}"}}}}}, } project = Project(isolation, config=config) environment = MockEnvironment( isolation, project.metadata, - 'default', - project.config.envs['default'], + "default", + project.config.envs["default"], {}, isolated_data_dir, isolated_data_dir, @@ -1771,19 +1771,19 @@ def test_verbosity_default(self, isolation, isolated_data_dir, platform, global_ global_application, ) - assert list(environment.expand_command('foo')) == ['command -v=9000'] + assert list(environment.expand_command("foo")) == ["command -v=9000"] def test_verbosity_unknown_modifier(self, isolation, isolated_data_dir, platform, global_application): config = { - 'project': {'name': 'my_app', 'version': '0.0.1'}, - 'tool': {'hatch': {'envs': {'default': {'scripts': {'foo': 'command {verbosity:bar}'}}}}}, + "project": {"name": "my_app", "version": "0.0.1"}, + "tool": {"hatch": {"envs": {"default": {"scripts": {"foo": "command {verbosity:bar}"}}}}}, } project = Project(isolation, config=config) environment = MockEnvironment( isolation, project.metadata, - 'default', - project.config.envs['default'], + "default", + project.config.envs["default"], {}, isolated_data_dir, isolated_data_dir, @@ -1792,20 +1792,20 @@ def test_verbosity_unknown_modifier(self, isolation, isolated_data_dir, platform global_application, ) - with pytest.raises(ValueError, match='Unknown verbosity modifier: bar'): - next(environment.expand_command('foo')) + with pytest.raises(ValueError, match="Unknown verbosity modifier: bar"): + next(environment.expand_command("foo")) def test_verbosity_flag_adjustment_not_integer(self, isolation, isolated_data_dir, platform, global_application): config = { - 'project': {'name': 'my_app', 'version': '0.0.1'}, - 'tool': {'hatch': {'envs': {'default': {'scripts': {'foo': 'command {verbosity:flag:-1.0}'}}}}}, + "project": {"name": "my_app", "version": "0.0.1"}, + "tool": {"hatch": {"envs": {"default": {"scripts": {"foo": "command {verbosity:flag:-1.0}"}}}}}, } project = Project(isolation, config=config) environment = MockEnvironment( isolation, project.metadata, - 'default', - project.config.envs['default'], + "default", + project.config.envs["default"], {}, isolated_data_dir, isolated_data_dir, @@ -1814,36 +1814,36 @@ def test_verbosity_flag_adjustment_not_integer(self, isolation, isolated_data_di global_application, ) - with pytest.raises(TypeError, match='Verbosity flag adjustment must be an integer: -1.0'): - next(environment.expand_command('foo')) + with pytest.raises(TypeError, match="Verbosity flag adjustment must be an integer: -1.0"): + next(environment.expand_command("foo")) @pytest.mark.parametrize( - ('verbosity', 'command'), + ("verbosity", "command"), [ - (-9000, 'command -qqq'), - (-3, 'command -qqq'), - (-2, 'command -qq'), - (-1, 'command -q'), - (0, 'command'), - (1, 'command -v'), - (2, 'command -vv'), - (3, 'command -vvv'), - (9000, 'command -vvv'), + (-9000, "command -qqq"), + (-3, "command -qqq"), + (-2, "command -qq"), + (-1, "command -q"), + (0, "command"), + (1, "command -v"), + (2, "command -vv"), + (3, "command -vvv"), + (9000, "command -vvv"), ], ) def test_verbosity_flag_default( self, isolation, isolated_data_dir, platform, global_application, verbosity, command ): config = { - 'project': {'name': 'my_app', 'version': '0.0.1'}, - 'tool': {'hatch': {'envs': {'default': {'scripts': {'foo': 'command {verbosity:flag}'}}}}}, + "project": {"name": "my_app", "version": "0.0.1"}, + "tool": {"hatch": {"envs": {"default": {"scripts": {"foo": "command {verbosity:flag}"}}}}}, } project = Project(isolation, config=config) environment = MockEnvironment( isolation, project.metadata, - 'default', - project.config.envs['default'], + "default", + project.config.envs["default"], {}, isolated_data_dir, isolated_data_dir, @@ -1852,35 +1852,35 @@ def test_verbosity_flag_default( global_application, ) - assert list(environment.expand_command('foo')) == [command] + assert list(environment.expand_command("foo")) == [command] @pytest.mark.parametrize( - ('adjustment', 'command'), + ("adjustment", "command"), [ - (-9000, 'command -qqq'), - (-3, 'command -qqq'), - (-2, 'command -qq'), - (-1, 'command -q'), - (0, 'command'), - (1, 'command -v'), - (2, 'command -vv'), - (3, 'command -vvv'), - (9000, 'command -vvv'), + (-9000, "command -qqq"), + (-3, "command -qqq"), + (-2, "command -qq"), + (-1, "command -q"), + (0, "command"), + (1, "command -v"), + (2, "command -vv"), + (3, "command -vvv"), + (9000, "command -vvv"), ], ) def test_verbosity_flag_adjustment( self, isolation, isolated_data_dir, platform, global_application, adjustment, command ): config = { - 'project': {'name': 'my_app', 'version': '0.0.1'}, - 'tool': {'hatch': {'envs': {'default': {'scripts': {'foo': f'command {{verbosity:flag:{adjustment}}}'}}}}}, + "project": {"name": "my_app", "version": "0.0.1"}, + "tool": {"hatch": {"envs": {"default": {"scripts": {"foo": f"command {{verbosity:flag:{adjustment}}}"}}}}}, } project = Project(isolation, config=config) environment = MockEnvironment( isolation, project.metadata, - 'default', - project.config.envs['default'], + "default", + project.config.envs["default"], {}, isolated_data_dir, isolated_data_dir, @@ -1889,19 +1889,19 @@ def test_verbosity_flag_adjustment( global_application, ) - assert list(environment.expand_command('foo')) == [command] + assert list(environment.expand_command("foo")) == [command] def test_args_undefined(self, isolation, isolated_data_dir, platform, global_application): config = { - 'project': {'name': 'my_app', 'version': '0.0.1'}, - 'tool': {'hatch': {'envs': {'default': {'scripts': {'foo': 'command {args}'}}}}}, + "project": {"name": "my_app", "version": "0.0.1"}, + "tool": {"hatch": {"envs": {"default": {"scripts": {"foo": "command {args}"}}}}}, } project = Project(isolation, config=config) environment = MockEnvironment( isolation, project.metadata, - 'default', - project.config.envs['default'], + "default", + project.config.envs["default"], {}, isolated_data_dir, isolated_data_dir, @@ -1910,19 +1910,19 @@ def test_args_undefined(self, isolation, isolated_data_dir, platform, global_app global_application, ) - assert list(environment.expand_command('foo')) == ['command'] + assert list(environment.expand_command("foo")) == ["command"] def test_args_default(self, isolation, isolated_data_dir, platform, global_application): config = { - 'project': {'name': 'my_app', 'version': '0.0.1'}, - 'tool': {'hatch': {'envs': {'default': {'scripts': {'foo': 'command {args: -bar > /dev/null}'}}}}}, + "project": {"name": "my_app", "version": "0.0.1"}, + "tool": {"hatch": {"envs": {"default": {"scripts": {"foo": "command {args: -bar > /dev/null}"}}}}}, } project = Project(isolation, config=config) environment = MockEnvironment( isolation, project.metadata, - 'default', - project.config.envs['default'], + "default", + project.config.envs["default"], {}, isolated_data_dir, isolated_data_dir, @@ -1931,19 +1931,19 @@ def test_args_default(self, isolation, isolated_data_dir, platform, global_appli global_application, ) - assert list(environment.expand_command('foo')) == ['command -bar > /dev/null'] + assert list(environment.expand_command("foo")) == ["command -bar > /dev/null"] def test_args_default_override(self, isolation, isolated_data_dir, platform, global_application): config = { - 'project': {'name': 'my_app', 'version': '0.0.1'}, - 'tool': {'hatch': {'envs': {'default': {'scripts': {'foo': 'command {args: -bar > /dev/null}'}}}}}, + "project": {"name": "my_app", "version": "0.0.1"}, + "tool": {"hatch": {"envs": {"default": {"scripts": {"foo": "command {args: -bar > /dev/null}"}}}}}, } project = Project(isolation, config=config) environment = MockEnvironment( isolation, project.metadata, - 'default', - project.config.envs['default'], + "default", + project.config.envs["default"], {}, isolated_data_dir, isolated_data_dir, @@ -1952,19 +1952,19 @@ def test_args_default_override(self, isolation, isolated_data_dir, platform, glo global_application, ) - assert list(environment.expand_command('foo baz')) == ['command baz'] + assert list(environment.expand_command("foo baz")) == ["command baz"] def test_matrix_no_selection(self, isolation, isolated_data_dir, platform, global_application): config = { - 'project': {'name': 'my_app', 'version': '0.0.1'}, - 'tool': {'hatch': {'envs': {'default': {'dependencies': ['pkg=={matrix}']}}}}, + "project": {"name": "my_app", "version": "0.0.1"}, + "tool": {"hatch": {"envs": {"default": {"dependencies": ["pkg=={matrix}"]}}}}, } project = Project(isolation, config=config) environment = MockEnvironment( isolation, project.metadata, - 'default', - project.config.envs['default'], + "default", + project.config.envs["default"], {}, isolated_data_dir, isolated_data_dir, @@ -1973,20 +1973,20 @@ def test_matrix_no_selection(self, isolation, isolated_data_dir, platform, globa global_application, ) - with pytest.raises(ValueError, match='The `matrix` context formatting field requires a modifier'): + with pytest.raises(ValueError, match="The `matrix` context formatting field requires a modifier"): _ = environment.dependencies def test_matrix_no_default(self, isolation, isolated_data_dir, platform, global_application): config = { - 'project': {'name': 'my_app', 'version': '0.0.1'}, - 'tool': {'hatch': {'envs': {'default': {'dependencies': ['pkg=={matrix:bar}']}}}}, + "project": {"name": "my_app", "version": "0.0.1"}, + "tool": {"hatch": {"envs": {"default": {"dependencies": ["pkg=={matrix:bar}"]}}}}, } project = Project(isolation, config=config) environment = MockEnvironment( isolation, project.metadata, - 'default', - project.config.envs['default'], + "default", + project.config.envs["default"], {}, isolated_data_dir, isolated_data_dir, @@ -1995,20 +1995,20 @@ def test_matrix_no_default(self, isolation, isolated_data_dir, platform, global_ global_application, ) - with pytest.raises(ValueError, match='Nonexistent matrix variable must set a default: bar'): + with pytest.raises(ValueError, match="Nonexistent matrix variable must set a default: bar"): _ = environment.dependencies def test_matrix_default(self, isolation, isolated_data_dir, platform, global_application): config = { - 'project': {'name': 'my_app', 'version': '0.0.1'}, - 'tool': {'hatch': {'envs': {'default': {'dependencies': ['pkg=={matrix:bar:9000}']}}}}, + "project": {"name": "my_app", "version": "0.0.1"}, + "tool": {"hatch": {"envs": {"default": {"dependencies": ["pkg=={matrix:bar:9000}"]}}}}, } project = Project(isolation, config=config) environment = MockEnvironment( isolation, project.metadata, - 'default', - project.config.envs['default'], + "default", + project.config.envs["default"], {}, isolated_data_dir, isolated_data_dir, @@ -2017,20 +2017,20 @@ def test_matrix_default(self, isolation, isolated_data_dir, platform, global_app global_application, ) - assert environment.dependencies == ['pkg==9000'] + assert environment.dependencies == ["pkg==9000"] def test_matrix_default_override(self, isolation, isolated_data_dir, platform, global_application): config = { - 'project': {'name': 'my_app', 'version': '0.0.1'}, - 'tool': {'hatch': {'envs': {'default': {'dependencies': ['pkg=={matrix:bar:baz}']}}}}, + "project": {"name": "my_app", "version": "0.0.1"}, + "tool": {"hatch": {"envs": {"default": {"dependencies": ["pkg=={matrix:bar:baz}"]}}}}, } project = Project(isolation, config=config) environment = MockEnvironment( isolation, project.metadata, - 'default', - project.config.envs['default'], - {'bar': '42'}, + "default", + project.config.envs["default"], + {"bar": "42"}, isolated_data_dir, isolated_data_dir, platform, @@ -2038,30 +2038,30 @@ def test_matrix_default_override(self, isolation, isolated_data_dir, platform, g global_application, ) - assert environment.dependencies == ['pkg==42'] + assert environment.dependencies == ["pkg==42"] def test_env_vars_override(self, isolation, isolated_data_dir, platform, global_application): config = { - 'project': {'name': 'my_app', 'version': '0.0.1'}, - 'tool': { - 'hatch': { - 'envs': { - 'default': { - 'dependencies': ['pkg{env:DEP_PIN}'], - 'env-vars': {'DEP_PIN': '==0.0.1'}, - 'overrides': {'env': {'DEP_ANY': {'env-vars': 'DEP_PIN='}}}, + "project": {"name": "my_app", "version": "0.0.1"}, + "tool": { + "hatch": { + "envs": { + "default": { + "dependencies": ["pkg{env:DEP_PIN}"], + "env-vars": {"DEP_PIN": "==0.0.1"}, + "overrides": {"env": {"DEP_ANY": {"env-vars": "DEP_PIN="}}}, }, }, }, }, } - with EnvVars({'DEP_ANY': 'true'}): + with EnvVars({"DEP_ANY": "true"}): project = Project(isolation, config=config) environment = MockEnvironment( isolation, project.metadata, - 'default', - project.config.envs['default'], + "default", + project.config.envs["default"], {}, isolated_data_dir, isolated_data_dir, @@ -2070,4 +2070,4 @@ def test_env_vars_override(self, isolation, isolated_data_dir, platform, global_ global_application, ) - assert environment.dependencies == ['pkg'] + assert environment.dependencies == ["pkg"] diff --git a/tests/helpers/helpers.py b/tests/helpers/helpers.py index a5557639e..52b9fdffd 100644 --- a/tests/helpers/helpers.py +++ b/tests/helpers/helpers.py @@ -29,17 +29,17 @@ def dedent(text): @lru_cache def tarfile_extraction_compat_options(): - return {'filter': 'data'} if sys.version_info >= (3, 12) else {} + return {"filter": "data"} if sys.version_info >= (3, 12) else {} def remove_trailing_spaces(text): - return ''.join(f'{line.rstrip()}\n' for line in text.splitlines(True)) + return "".join(f"{line.rstrip()}\n" for line in text.splitlines(True)) def extract_requirements(lines): for raw_line in lines: line = raw_line.rstrip() - if line and not line.startswith('#'): + if line and not line.startswith("#"): yield line @@ -50,11 +50,11 @@ def get_current_timestamp(): def assert_plugin_installation(subprocess_run, dependencies: list[str], *, verbosity=0, count=1): command = [ sys.executable, - '-u', - '-m', - 'pip', - 'install', - '--disable-pip-version-check', + "-u", + "-m", + "pip", + "install", + "--disable-pip-version-check", ] add_verbosity_flag(command, verbosity, adjustment=-1) command.extend(dependencies) @@ -71,8 +71,8 @@ def assert_files(directory, expected_files, *, check_contents=True): relative_path = os.path.relpath(root, start) # First iteration - if relative_path == '.': - relative_path = '' + if relative_path == ".": + relative_path = "" for file_name in files: relative_file_path = os.path.join(relative_path, file_name) @@ -82,10 +82,10 @@ def assert_files(directory, expected_files, *, check_contents=True): file_path = os.path.join(start, relative_file_path) expected_contents = expected_relative_files[relative_file_path] try: - with open(file_path, encoding='utf-8') as f: + with open(file_path, encoding="utf-8") as f: assert f.read() == expected_contents, relative_file_path except UnicodeDecodeError: - with open(file_path, 'rb') as f: + with open(file_path, "rb") as f: assert f.read() == expected_contents, (relative_file_path, expected_contents) else: # no cov pass @@ -93,10 +93,10 @@ def assert_files(directory, expected_files, *, check_contents=True): expected_relative_file_paths = set(expected_relative_files) missing_files = expected_relative_file_paths - seen_relative_file_paths - assert not missing_files, f'Missing files: {", ".join(sorted(missing_files))}' + assert not missing_files, f"Missing files: {', '.join(sorted(missing_files))}" extra_files = seen_relative_file_paths - expected_relative_file_paths - assert not extra_files, f'Extra files: {", ".join(sorted(extra_files))}' + assert not extra_files, f"Extra files: {', '.join(sorted(extra_files))}" def assert_output_match(output: str, pattern: str, *, exact: bool = True): @@ -105,35 +105,35 @@ def assert_output_match(output: str, pattern: str, *, exact: bool = True): def get_template_files(template_name, project_name, **kwargs): - kwargs['project_name'] = project_name - kwargs['project_name_normalized'] = project_name.lower().replace('.', '-') - kwargs['package_name'] = kwargs['project_name_normalized'].replace('-', '_') + kwargs["project_name"] = project_name + kwargs["project_name_normalized"] = project_name.lower().replace(".", "-") + kwargs["package_name"] = kwargs["project_name_normalized"].replace("-", "_") config = RootConfig({}) - kwargs.setdefault('author', config.template.name) - kwargs.setdefault('email', config.template.email) - kwargs.setdefault('year', str(datetime.now(timezone.utc).year)) + kwargs.setdefault("author", config.template.name) + kwargs.setdefault("email", config.template.email) + kwargs.setdefault("year", str(datetime.now(timezone.utc).year)) return __load_template_module(template_name)(**kwargs) @lru_cache def __load_template_module(template_name): - template = importlib.import_module(f'..templates.{template_name}', __name__) + template = importlib.import_module(f"..templates.{template_name}", __name__) return template.get_files def update_project_environment(project, name, config): - project_file = project.root / 'pyproject.toml' + project_file = project.root / "pyproject.toml" raw_config = load_toml_file(str(project_file)) - env_config = raw_config.setdefault('tool', {}).setdefault('hatch', {}).setdefault('envs', {}).setdefault(name, {}) + env_config = raw_config.setdefault("tool", {}).setdefault("hatch", {}).setdefault("envs", {}).setdefault(name, {}) env_config.update(config) - project.config.envs[name] = project.config.envs.get(name, project.config.envs['default']).copy() + project.config.envs[name] = project.config.envs.get(name, project.config.envs["default"]).copy() project.config.envs[name].update(env_config) - with open(str(project_file), 'w', encoding='utf-8') as f: + with open(str(project_file), "w", encoding="utf-8") as f: f.write(tomli_w.dumps(raw_config)) @@ -145,7 +145,7 @@ def write_distribution(directory: Path, name: str): python_path.parent.ensure_dir_exists() python_path.touch() - metadata = {'source': dist.source, 'python_path': dist.python_path} + metadata = {"source": dist.source, "python_path": dist.python_path} metadata_file = path / InstalledDistribution.metadata_filename() metadata_file.write_text(json.dumps(metadata)) @@ -157,18 +157,18 @@ def downgrade_distribution_metadata(dist_dir: Path): metadata = json.loads(metadata_file.read_text()) dist = InstalledDistribution(dist_dir, get_distribution(dist_dir.name), metadata) - source = metadata['source'] - python_path = metadata['python_path'] + source = metadata["source"] + python_path = metadata["python_path"] version = dist.version new_version = downgrade_version(version) new_source = source.replace(version, new_version) - metadata['source'] = new_source + metadata["source"] = new_source # We also modify the Python path because some directory structures are determined # by the archive name which is itself determined by the source - metadata['python_path'] = python_path.replace(version, new_version) - if python_path != metadata['python_path']: - new_python_path = dist_dir / metadata['python_path'] + metadata["python_path"] = python_path.replace(version, new_version) + if python_path != metadata["python_path"]: + new_python_path = dist_dir / metadata["python_path"] new_python_path.parent.ensure_dir_exists() (dist_dir / python_path).rename(new_python_path) @@ -177,5 +177,5 @@ def downgrade_distribution_metadata(dist_dir: Path): def downgrade_version(version: str) -> str: - major_version = version.split('.')[0] + major_version = version.split(".")[0] return version.replace(major_version, str(int(major_version) - 1), 1) diff --git a/tests/helpers/templates/new/basic.py b/tests/helpers/templates/new/basic.py index 61c1dc81f..1494828f7 100644 --- a/tests/helpers/templates/new/basic.py +++ b/tests/helpers/templates/new/basic.py @@ -7,35 +7,35 @@ def get_files(**kwargs): return [ File( - Path('LICENSE.txt'), - MIT.replace('', f"{kwargs['year']}-present", 1).replace( - '', f"{kwargs['author']} <{kwargs['email']}>", 1 + Path("LICENSE.txt"), + MIT.replace("", f"{kwargs['year']}-present", 1).replace( + "", f"{kwargs['author']} <{kwargs['email']}>", 1 ), ), File( - Path('src', kwargs['package_name'], '__init__.py'), + Path("src", kwargs["package_name"], "__init__.py"), f"""\ -# SPDX-FileCopyrightText: {kwargs['year']}-present {kwargs['author']} <{kwargs['email']}> +# SPDX-FileCopyrightText: {kwargs["year"]}-present {kwargs["author"]} <{kwargs["email"]}> # # SPDX-License-Identifier: MIT """, ), File( - Path('src', kwargs['package_name'], '__about__.py'), + Path("src", kwargs["package_name"], "__about__.py"), f"""\ -# SPDX-FileCopyrightText: {kwargs['year']}-present {kwargs['author']} <{kwargs['email']}> +# SPDX-FileCopyrightText: {kwargs["year"]}-present {kwargs["author"]} <{kwargs["email"]}> # # SPDX-License-Identifier: MIT __version__ = "0.0.1" """, ), File( - Path('README.md'), + Path("README.md"), f"""\ -# {kwargs['project_name']} +# {kwargs["project_name"]} -[![PyPI - Version](https://img.shields.io/pypi/v/{kwargs['project_name_normalized']}.svg)](https://pypi.org/project/{kwargs['project_name_normalized']}) -[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/{kwargs['project_name_normalized']}.svg)](https://pypi.org/project/{kwargs['project_name_normalized']}) +[![PyPI - Version](https://img.shields.io/pypi/v/{kwargs["project_name_normalized"]}.svg)](https://pypi.org/project/{kwargs["project_name_normalized"]}) +[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/{kwargs["project_name_normalized"]}.svg)](https://pypi.org/project/{kwargs["project_name_normalized"]}) ----- @@ -47,23 +47,23 @@ def get_files(**kwargs): ## Installation ```console -pip install {kwargs['project_name_normalized']} +pip install {kwargs["project_name_normalized"]} ``` ## License -`{kwargs['project_name_normalized']}` is distributed under the terms of the [MIT](https://spdx.org/licenses/MIT.html) license. +`{kwargs["project_name_normalized"]}` is distributed under the terms of the [MIT](https://spdx.org/licenses/MIT.html) license. """, ), File( - Path('pyproject.toml'), + Path("pyproject.toml"), f"""\ [build-system] requires = ["hatchling"] build-backend = "hatchling.build" [project] -name = "{kwargs['project_name_normalized']}" +name = "{kwargs["project_name_normalized"]}" dynamic = ["version"] description = '' readme = "README.md" @@ -71,7 +71,7 @@ def get_files(**kwargs): license = "MIT" keywords = [] authors = [ - {{ name = "{kwargs['author']}", email = "{kwargs['email']}" }}, + {{ name = "{kwargs["author"]}", email = "{kwargs["email"]}" }}, ] classifiers = [ "Development Status :: 4 - Beta", @@ -87,12 +87,12 @@ def get_files(**kwargs): dependencies = [] [project.urls] -Documentation = "https://github.com/{kwargs['author']}/{kwargs['project_name_normalized']}#readme" -Issues = "https://github.com/{kwargs['author']}/{kwargs['project_name_normalized']}/issues" -Source = "https://github.com/{kwargs['author']}/{kwargs['project_name_normalized']}" +Documentation = "https://github.com/{kwargs["author"]}/{kwargs["project_name_normalized"]}#readme" +Issues = "https://github.com/{kwargs["author"]}/{kwargs["project_name_normalized"]}/issues" +Source = "https://github.com/{kwargs["author"]}/{kwargs["project_name_normalized"]}" [tool.hatch.version] -path = "src/{kwargs['package_name']}/__about__.py" +path = "src/{kwargs["package_name"]}/__about__.py" """, ), ] diff --git a/tests/helpers/templates/new/default.py b/tests/helpers/templates/new/default.py index 0cf6c0ef8..596eaa60a 100644 --- a/tests/helpers/templates/new/default.py +++ b/tests/helpers/templates/new/default.py @@ -5,47 +5,47 @@ def get_files(**kwargs): - description = kwargs.get('description', '') + description = kwargs.get("description", "") return [ File( - Path('LICENSE.txt'), - MIT.replace('', f"{kwargs['year']}-present", 1).replace( - '', f"{kwargs['author']} <{kwargs['email']}>", 1 + Path("LICENSE.txt"), + MIT.replace("", f"{kwargs['year']}-present", 1).replace( + "", f"{kwargs['author']} <{kwargs['email']}>", 1 ), ), File( - Path('src', kwargs['package_name'], '__init__.py'), + Path("src", kwargs["package_name"], "__init__.py"), f"""\ -# SPDX-FileCopyrightText: {kwargs['year']}-present {kwargs['author']} <{kwargs['email']}> +# SPDX-FileCopyrightText: {kwargs["year"]}-present {kwargs["author"]} <{kwargs["email"]}> # # SPDX-License-Identifier: MIT """, ), File( - Path('src', kwargs['package_name'], '__about__.py'), + Path("src", kwargs["package_name"], "__about__.py"), f"""\ -# SPDX-FileCopyrightText: {kwargs['year']}-present {kwargs['author']} <{kwargs['email']}> +# SPDX-FileCopyrightText: {kwargs["year"]}-present {kwargs["author"]} <{kwargs["email"]}> # # SPDX-License-Identifier: MIT __version__ = "0.0.1" """, ), File( - Path('tests', '__init__.py'), + Path("tests", "__init__.py"), f"""\ -# SPDX-FileCopyrightText: {kwargs['year']}-present {kwargs['author']} <{kwargs['email']}> +# SPDX-FileCopyrightText: {kwargs["year"]}-present {kwargs["author"]} <{kwargs["email"]}> # # SPDX-License-Identifier: MIT """, ), File( - Path('README.md'), + Path("README.md"), f"""\ -# {kwargs['project_name']} +# {kwargs["project_name"]} -[![PyPI - Version](https://img.shields.io/pypi/v/{kwargs['project_name_normalized']}.svg)](https://pypi.org/project/{kwargs['project_name_normalized']}) -[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/{kwargs['project_name_normalized']}.svg)](https://pypi.org/project/{kwargs['project_name_normalized']}) +[![PyPI - Version](https://img.shields.io/pypi/v/{kwargs["project_name_normalized"]}.svg)](https://pypi.org/project/{kwargs["project_name_normalized"]}) +[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/{kwargs["project_name_normalized"]}.svg)](https://pypi.org/project/{kwargs["project_name_normalized"]}) ----- @@ -57,23 +57,23 @@ def get_files(**kwargs): ## Installation ```console -pip install {kwargs['project_name_normalized']} +pip install {kwargs["project_name_normalized"]} ``` ## License -`{kwargs['project_name_normalized']}` is distributed under the terms of the [MIT](https://spdx.org/licenses/MIT.html) license. +`{kwargs["project_name_normalized"]}` is distributed under the terms of the [MIT](https://spdx.org/licenses/MIT.html) license. """, ), File( - Path('pyproject.toml'), + Path("pyproject.toml"), f"""\ [build-system] requires = ["hatchling"] build-backend = "hatchling.build" [project] -name = "{kwargs['project_name_normalized']}" +name = "{kwargs["project_name_normalized"]}" dynamic = ["version"] description = '{description}' readme = "README.md" @@ -81,7 +81,7 @@ def get_files(**kwargs): license = "MIT" keywords = [] authors = [ - {{ name = "{kwargs['author']}", email = "{kwargs['email']}" }}, + {{ name = "{kwargs["author"]}", email = "{kwargs["email"]}" }}, ] classifiers = [ "Development Status :: 4 - Beta", @@ -97,31 +97,31 @@ def get_files(**kwargs): dependencies = [] [project.urls] -Documentation = "https://github.com/{kwargs['author']}/{kwargs['project_name_normalized']}#readme" -Issues = "https://github.com/{kwargs['author']}/{kwargs['project_name_normalized']}/issues" -Source = "https://github.com/{kwargs['author']}/{kwargs['project_name_normalized']}" +Documentation = "https://github.com/{kwargs["author"]}/{kwargs["project_name_normalized"]}#readme" +Issues = "https://github.com/{kwargs["author"]}/{kwargs["project_name_normalized"]}/issues" +Source = "https://github.com/{kwargs["author"]}/{kwargs["project_name_normalized"]}" [tool.hatch.version] -path = "src/{kwargs['package_name']}/__about__.py" +path = "src/{kwargs["package_name"]}/__about__.py" [tool.hatch.envs.types] extra-dependencies = [ "mypy>=1.0.0", ] [tool.hatch.envs.types.scripts] -check = "mypy --install-types --non-interactive {{args:src/{kwargs['package_name']} tests}}" +check = "mypy --install-types --non-interactive {{args:src/{kwargs["package_name"]} tests}}" [tool.coverage.run] -source_pkgs = ["{kwargs['package_name']}", "tests"] +source_pkgs = ["{kwargs["package_name"]}", "tests"] branch = true parallel = true omit = [ - "src/{kwargs['package_name']}/__about__.py", + "src/{kwargs["package_name"]}/__about__.py", ] [tool.coverage.paths] -{kwargs['package_name']} = ["src/{kwargs['package_name']}", "*/{kwargs['project_name_normalized']}/src/{kwargs['package_name']}"] -tests = ["tests", "*/{kwargs['project_name_normalized']}/tests"] +{kwargs["package_name"]} = ["src/{kwargs["package_name"]}", "*/{kwargs["project_name_normalized"]}/src/{kwargs["package_name"]}"] +tests = ["tests", "*/{kwargs["project_name_normalized"]}/tests"] [tool.coverage.report] exclude_lines = [ diff --git a/tests/helpers/templates/new/feature_ci.py b/tests/helpers/templates/new/feature_ci.py index 519371cff..bd6f22c82 100644 --- a/tests/helpers/templates/new/feature_ci.py +++ b/tests/helpers/templates/new/feature_ci.py @@ -8,7 +8,7 @@ def get_files(**kwargs): files = [File(Path(f.path), f.contents) for f in get_template_files(**kwargs)] files.append( File( - Path('.github', 'workflows', 'test.yml'), + Path(".github", "workflows", "test.yml"), """\ name: test diff --git a/tests/helpers/templates/new/feature_cli.py b/tests/helpers/templates/new/feature_cli.py index 8fe649a64..3213af3a5 100644 --- a/tests/helpers/templates/new/feature_cli.py +++ b/tests/helpers/templates/new/feature_cli.py @@ -7,74 +7,74 @@ def get_files(**kwargs): return [ File( - Path('LICENSE.txt'), - MIT.replace('', f"{kwargs['year']}-present", 1).replace( - '', f"{kwargs['author']} <{kwargs['email']}>", 1 + Path("LICENSE.txt"), + MIT.replace("", f"{kwargs['year']}-present", 1).replace( + "", f"{kwargs['author']} <{kwargs['email']}>", 1 ), ), File( - Path('src', kwargs['package_name'], '__init__.py'), + Path("src", kwargs["package_name"], "__init__.py"), f"""\ -# SPDX-FileCopyrightText: {kwargs['year']}-present {kwargs['author']} <{kwargs['email']}> +# SPDX-FileCopyrightText: {kwargs["year"]}-present {kwargs["author"]} <{kwargs["email"]}> # # SPDX-License-Identifier: MIT """, ), File( - Path('src', kwargs['package_name'], '__about__.py'), + Path("src", kwargs["package_name"], "__about__.py"), f"""\ -# SPDX-FileCopyrightText: {kwargs['year']}-present {kwargs['author']} <{kwargs['email']}> +# SPDX-FileCopyrightText: {kwargs["year"]}-present {kwargs["author"]} <{kwargs["email"]}> # # SPDX-License-Identifier: MIT __version__ = "0.0.1" """, ), File( - Path('src', kwargs['package_name'], '__main__.py'), + Path("src", kwargs["package_name"], "__main__.py"), f"""\ -# SPDX-FileCopyrightText: {kwargs['year']}-present {kwargs['author']} <{kwargs['email']}> +# SPDX-FileCopyrightText: {kwargs["year"]}-present {kwargs["author"]} <{kwargs["email"]}> # # SPDX-License-Identifier: MIT import sys if __name__ == "__main__": - from {kwargs['package_name']}.cli import {kwargs['package_name']} + from {kwargs["package_name"]}.cli import {kwargs["package_name"]} - sys.exit({kwargs['package_name']}()) + sys.exit({kwargs["package_name"]}()) """, ), File( - Path('src', kwargs['package_name'], 'cli', '__init__.py'), + Path("src", kwargs["package_name"], "cli", "__init__.py"), f"""\ -# SPDX-FileCopyrightText: {kwargs['year']}-present {kwargs['author']} <{kwargs['email']}> +# SPDX-FileCopyrightText: {kwargs["year"]}-present {kwargs["author"]} <{kwargs["email"]}> # # SPDX-License-Identifier: MIT import click -from {kwargs['package_name']}.__about__ import __version__ +from {kwargs["package_name"]}.__about__ import __version__ @click.group(context_settings={{"help_option_names": ["-h", "--help"]}}, invoke_without_command=True) -@click.version_option(version=__version__, prog_name="{kwargs['project_name']}") -def {kwargs['package_name']}(): +@click.version_option(version=__version__, prog_name="{kwargs["project_name"]}") +def {kwargs["package_name"]}(): click.echo("Hello world!") """, ), File( - Path('tests', '__init__.py'), + Path("tests", "__init__.py"), f"""\ -# SPDX-FileCopyrightText: {kwargs['year']}-present {kwargs['author']} <{kwargs['email']}> +# SPDX-FileCopyrightText: {kwargs["year"]}-present {kwargs["author"]} <{kwargs["email"]}> # # SPDX-License-Identifier: MIT """, ), File( - Path('README.md'), + Path("README.md"), f"""\ -# {kwargs['project_name']} +# {kwargs["project_name"]} -[![PyPI - Version](https://img.shields.io/pypi/v/{kwargs['project_name_normalized']}.svg)](https://pypi.org/project/{kwargs['project_name_normalized']}) -[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/{kwargs['project_name_normalized']}.svg)](https://pypi.org/project/{kwargs['project_name_normalized']}) +[![PyPI - Version](https://img.shields.io/pypi/v/{kwargs["project_name_normalized"]}.svg)](https://pypi.org/project/{kwargs["project_name_normalized"]}) +[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/{kwargs["project_name_normalized"]}.svg)](https://pypi.org/project/{kwargs["project_name_normalized"]}) ----- @@ -86,23 +86,23 @@ def {kwargs['package_name']}(): ## Installation ```console -pip install {kwargs['project_name_normalized']} +pip install {kwargs["project_name_normalized"]} ``` ## License -`{kwargs['project_name_normalized']}` is distributed under the terms of the [MIT](https://spdx.org/licenses/MIT.html) license. +`{kwargs["project_name_normalized"]}` is distributed under the terms of the [MIT](https://spdx.org/licenses/MIT.html) license. """, ), File( - Path('pyproject.toml'), + Path("pyproject.toml"), f"""\ [build-system] requires = ["hatchling"] build-backend = "hatchling.build" [project] -name = "{kwargs['project_name_normalized']}" +name = "{kwargs["project_name_normalized"]}" dynamic = ["version"] description = '' readme = "README.md" @@ -110,7 +110,7 @@ def {kwargs['package_name']}(): license = "MIT" keywords = [] authors = [ - {{ name = "{kwargs['author']}", email = "{kwargs['email']}" }}, + {{ name = "{kwargs["author"]}", email = "{kwargs["email"]}" }}, ] classifiers = [ "Development Status :: 4 - Beta", @@ -128,34 +128,34 @@ def {kwargs['package_name']}(): ] [project.urls] -Documentation = "https://github.com/{kwargs['author']}/{kwargs['project_name_normalized']}#readme" -Issues = "https://github.com/{kwargs['author']}/{kwargs['project_name_normalized']}/issues" -Source = "https://github.com/{kwargs['author']}/{kwargs['project_name_normalized']}" +Documentation = "https://github.com/{kwargs["author"]}/{kwargs["project_name_normalized"]}#readme" +Issues = "https://github.com/{kwargs["author"]}/{kwargs["project_name_normalized"]}/issues" +Source = "https://github.com/{kwargs["author"]}/{kwargs["project_name_normalized"]}" [project.scripts] -{kwargs['project_name_normalized']} = "{kwargs['package_name']}.cli:{kwargs['package_name']}" +{kwargs["project_name_normalized"]} = "{kwargs["package_name"]}.cli:{kwargs["package_name"]}" [tool.hatch.version] -path = "src/{kwargs['package_name']}/__about__.py" +path = "src/{kwargs["package_name"]}/__about__.py" [tool.hatch.envs.types] extra-dependencies = [ "mypy>=1.0.0", ] [tool.hatch.envs.types.scripts] -check = "mypy --install-types --non-interactive {{args:src/{kwargs['package_name']} tests}}" +check = "mypy --install-types --non-interactive {{args:src/{kwargs["package_name"]} tests}}" [tool.coverage.run] -source_pkgs = ["{kwargs['package_name']}", "tests"] +source_pkgs = ["{kwargs["package_name"]}", "tests"] branch = true parallel = true omit = [ - "src/{kwargs['package_name']}/__about__.py", + "src/{kwargs["package_name"]}/__about__.py", ] [tool.coverage.paths] -{kwargs['package_name']} = ["src/{kwargs['package_name']}", "*/{kwargs['project_name_normalized']}/src/{kwargs['package_name']}"] -tests = ["tests", "*/{kwargs['project_name_normalized']}/tests"] +{kwargs["package_name"]} = ["src/{kwargs["package_name"]}", "*/{kwargs["project_name_normalized"]}/src/{kwargs["package_name"]}"] +tests = ["tests", "*/{kwargs["project_name_normalized"]}/tests"] [tool.coverage.report] exclude_lines = [ diff --git a/tests/helpers/templates/new/feature_no_src_layout.py b/tests/helpers/templates/new/feature_no_src_layout.py index 630da3286..3464644b1 100644 --- a/tests/helpers/templates/new/feature_no_src_layout.py +++ b/tests/helpers/templates/new/feature_no_src_layout.py @@ -5,47 +5,47 @@ def get_files(**kwargs): - description = kwargs.get('description', '') + description = kwargs.get("description", "") return [ File( - Path('LICENSE.txt'), - MIT.replace('', f"{kwargs['year']}-present", 1).replace( - '', f"{kwargs['author']} <{kwargs['email']}>", 1 + Path("LICENSE.txt"), + MIT.replace("", f"{kwargs['year']}-present", 1).replace( + "", f"{kwargs['author']} <{kwargs['email']}>", 1 ), ), File( - Path(kwargs['package_name'], '__init__.py'), + Path(kwargs["package_name"], "__init__.py"), f"""\ -# SPDX-FileCopyrightText: {kwargs['year']}-present {kwargs['author']} <{kwargs['email']}> +# SPDX-FileCopyrightText: {kwargs["year"]}-present {kwargs["author"]} <{kwargs["email"]}> # # SPDX-License-Identifier: MIT """, ), File( - Path(kwargs['package_name'], '__about__.py'), + Path(kwargs["package_name"], "__about__.py"), f"""\ -# SPDX-FileCopyrightText: {kwargs['year']}-present {kwargs['author']} <{kwargs['email']}> +# SPDX-FileCopyrightText: {kwargs["year"]}-present {kwargs["author"]} <{kwargs["email"]}> # # SPDX-License-Identifier: MIT __version__ = "0.0.1" """, ), File( - Path('tests', '__init__.py'), + Path("tests", "__init__.py"), f"""\ -# SPDX-FileCopyrightText: {kwargs['year']}-present {kwargs['author']} <{kwargs['email']}> +# SPDX-FileCopyrightText: {kwargs["year"]}-present {kwargs["author"]} <{kwargs["email"]}> # # SPDX-License-Identifier: MIT """, ), File( - Path('README.md'), + Path("README.md"), f"""\ -# {kwargs['project_name']} +# {kwargs["project_name"]} -[![PyPI - Version](https://img.shields.io/pypi/v/{kwargs['project_name_normalized']}.svg)](https://pypi.org/project/{kwargs['project_name_normalized']}) -[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/{kwargs['project_name_normalized']}.svg)](https://pypi.org/project/{kwargs['project_name_normalized']}) +[![PyPI - Version](https://img.shields.io/pypi/v/{kwargs["project_name_normalized"]}.svg)](https://pypi.org/project/{kwargs["project_name_normalized"]}) +[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/{kwargs["project_name_normalized"]}.svg)](https://pypi.org/project/{kwargs["project_name_normalized"]}) ----- @@ -57,23 +57,23 @@ def get_files(**kwargs): ## Installation ```console -pip install {kwargs['project_name_normalized']} +pip install {kwargs["project_name_normalized"]} ``` ## License -`{kwargs['project_name_normalized']}` is distributed under the terms of the [MIT](https://spdx.org/licenses/MIT.html) license. +`{kwargs["project_name_normalized"]}` is distributed under the terms of the [MIT](https://spdx.org/licenses/MIT.html) license. """, ), File( - Path('pyproject.toml'), + Path("pyproject.toml"), f"""\ [build-system] requires = ["hatchling"] build-backend = "hatchling.build" [project] -name = "{kwargs['project_name_normalized']}" +name = "{kwargs["project_name_normalized"]}" dynamic = ["version"] description = '{description}' readme = "README.md" @@ -81,7 +81,7 @@ def get_files(**kwargs): license = "MIT" keywords = [] authors = [ - {{ name = "{kwargs['author']}", email = "{kwargs['email']}" }}, + {{ name = "{kwargs["author"]}", email = "{kwargs["email"]}" }}, ] classifiers = [ "Development Status :: 4 - Beta", @@ -97,31 +97,31 @@ def get_files(**kwargs): dependencies = [] [project.urls] -Documentation = "https://github.com/{kwargs['author']}/{kwargs['project_name_normalized']}#readme" -Issues = "https://github.com/{kwargs['author']}/{kwargs['project_name_normalized']}/issues" -Source = "https://github.com/{kwargs['author']}/{kwargs['project_name_normalized']}" +Documentation = "https://github.com/{kwargs["author"]}/{kwargs["project_name_normalized"]}#readme" +Issues = "https://github.com/{kwargs["author"]}/{kwargs["project_name_normalized"]}/issues" +Source = "https://github.com/{kwargs["author"]}/{kwargs["project_name_normalized"]}" [tool.hatch.version] -path = "{kwargs['package_name']}/__about__.py" +path = "{kwargs["package_name"]}/__about__.py" [tool.hatch.envs.types] extra-dependencies = [ "mypy>=1.0.0", ] [tool.hatch.envs.types.scripts] -check = "mypy --install-types --non-interactive {{args:{kwargs['package_name']} tests}}" +check = "mypy --install-types --non-interactive {{args:{kwargs["package_name"]} tests}}" [tool.coverage.run] -source_pkgs = ["{kwargs['package_name']}", "tests"] +source_pkgs = ["{kwargs["package_name"]}", "tests"] branch = true parallel = true omit = [ - "{kwargs['package_name']}/__about__.py", + "{kwargs["package_name"]}/__about__.py", ] [tool.coverage.paths] -{kwargs['package_name']} = ["{kwargs['package_name']}", "*/{kwargs['project_name_normalized']}/{kwargs['package_name']}"] -tests = ["tests", "*/{kwargs['project_name_normalized']}/tests"] +{kwargs["package_name"]} = ["{kwargs["package_name"]}", "*/{kwargs["project_name_normalized"]}/{kwargs["package_name"]}"] +tests = ["tests", "*/{kwargs["project_name_normalized"]}/tests"] [tool.coverage.report] exclude_lines = [ diff --git a/tests/helpers/templates/new/licenses_empty.py b/tests/helpers/templates/new/licenses_empty.py index eac18be04..9b65440b0 100644 --- a/tests/helpers/templates/new/licenses_empty.py +++ b/tests/helpers/templates/new/licenses_empty.py @@ -4,16 +4,16 @@ def get_files(**kwargs): return [ - File(Path('src', kwargs['package_name'], '__init__.py')), - File(Path('src', kwargs['package_name'], '__about__.py'), '__version__ = "0.0.1"\n'), - File(Path('tests', '__init__.py')), + File(Path("src", kwargs["package_name"], "__init__.py")), + File(Path("src", kwargs["package_name"], "__about__.py"), '__version__ = "0.0.1"\n'), + File(Path("tests", "__init__.py")), File( - Path('README.md'), + Path("README.md"), f"""\ -# {kwargs['project_name']} +# {kwargs["project_name"]} -[![PyPI - Version](https://img.shields.io/pypi/v/{kwargs['project_name_normalized']}.svg)](https://pypi.org/project/{kwargs['project_name_normalized']}) -[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/{kwargs['project_name_normalized']}.svg)](https://pypi.org/project/{kwargs['project_name_normalized']}) +[![PyPI - Version](https://img.shields.io/pypi/v/{kwargs["project_name_normalized"]}.svg)](https://pypi.org/project/{kwargs["project_name_normalized"]}) +[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/{kwargs["project_name_normalized"]}.svg)](https://pypi.org/project/{kwargs["project_name_normalized"]}) ----- @@ -24,19 +24,19 @@ def get_files(**kwargs): ## Installation ```console -pip install {kwargs['project_name_normalized']} +pip install {kwargs["project_name_normalized"]} ``` """, ), File( - Path('pyproject.toml'), + Path("pyproject.toml"), f"""\ [build-system] requires = ["hatchling"] build-backend = "hatchling.build" [project] -name = "{kwargs['project_name_normalized']}" +name = "{kwargs["project_name_normalized"]}" dynamic = ["version"] description = '' readme = "README.md" @@ -44,7 +44,7 @@ def get_files(**kwargs): license = "" keywords = [] authors = [ - {{ name = "{kwargs['author']}", email = "{kwargs['email']}" }}, + {{ name = "{kwargs["author"]}", email = "{kwargs["email"]}" }}, ] classifiers = [ "Development Status :: 4 - Beta", @@ -60,31 +60,31 @@ def get_files(**kwargs): dependencies = [] [project.urls] -Documentation = "https://github.com/{kwargs['author']}/{kwargs['project_name_normalized']}#readme" -Issues = "https://github.com/{kwargs['author']}/{kwargs['project_name_normalized']}/issues" -Source = "https://github.com/{kwargs['author']}/{kwargs['project_name_normalized']}" +Documentation = "https://github.com/{kwargs["author"]}/{kwargs["project_name_normalized"]}#readme" +Issues = "https://github.com/{kwargs["author"]}/{kwargs["project_name_normalized"]}/issues" +Source = "https://github.com/{kwargs["author"]}/{kwargs["project_name_normalized"]}" [tool.hatch.version] -path = "src/{kwargs['package_name']}/__about__.py" +path = "src/{kwargs["package_name"]}/__about__.py" [tool.hatch.envs.types] extra-dependencies = [ "mypy>=1.0.0", ] [tool.hatch.envs.types.scripts] -check = "mypy --install-types --non-interactive {{args:src/{kwargs['package_name']} tests}}" +check = "mypy --install-types --non-interactive {{args:src/{kwargs["package_name"]} tests}}" [tool.coverage.run] -source_pkgs = ["{kwargs['package_name']}", "tests"] +source_pkgs = ["{kwargs["package_name"]}", "tests"] branch = true parallel = true omit = [ - "src/{kwargs['package_name']}/__about__.py", + "src/{kwargs["package_name"]}/__about__.py", ] [tool.coverage.paths] -{kwargs['package_name']} = ["src/{kwargs['package_name']}", "*/{kwargs['project_name_normalized']}/src/{kwargs['package_name']}"] -tests = ["tests", "*/{kwargs['project_name_normalized']}/tests"] +{kwargs["package_name"]} = ["src/{kwargs["package_name"]}", "*/{kwargs["project_name_normalized"]}/src/{kwargs["package_name"]}"] +tests = ["tests", "*/{kwargs["project_name_normalized"]}/tests"] [tool.coverage.report] exclude_lines = [ diff --git a/tests/helpers/templates/new/licenses_multiple.py b/tests/helpers/templates/new/licenses_multiple.py index 45aefce6b..af9f11e62 100644 --- a/tests/helpers/templates/new/licenses_multiple.py +++ b/tests/helpers/templates/new/licenses_multiple.py @@ -6,45 +6,45 @@ def get_files(**kwargs): return [ - File(Path('LICENSES', 'Apache-2.0.txt'), Apache_2_0), + File(Path("LICENSES", "Apache-2.0.txt"), Apache_2_0), File( - Path('LICENSES', 'MIT.txt'), - MIT.replace('', f"{kwargs['year']}-present", 1).replace( - '', f"{kwargs['author']} <{kwargs['email']}>", 1 + Path("LICENSES", "MIT.txt"), + MIT.replace("", f"{kwargs['year']}-present", 1).replace( + "", f"{kwargs['author']} <{kwargs['email']}>", 1 ), ), File( - Path('src', kwargs['package_name'], '__init__.py'), + Path("src", kwargs["package_name"], "__init__.py"), f"""\ -# SPDX-FileCopyrightText: {kwargs['year']}-present {kwargs['author']} <{kwargs['email']}> +# SPDX-FileCopyrightText: {kwargs["year"]}-present {kwargs["author"]} <{kwargs["email"]}> # # SPDX-License-Identifier: Apache-2.0 OR MIT """, ), File( - Path('src', kwargs['package_name'], '__about__.py'), + Path("src", kwargs["package_name"], "__about__.py"), f"""\ -# SPDX-FileCopyrightText: {kwargs['year']}-present {kwargs['author']} <{kwargs['email']}> +# SPDX-FileCopyrightText: {kwargs["year"]}-present {kwargs["author"]} <{kwargs["email"]}> # # SPDX-License-Identifier: Apache-2.0 OR MIT __version__ = "0.0.1" """, ), File( - Path('tests', '__init__.py'), + Path("tests", "__init__.py"), f"""\ -# SPDX-FileCopyrightText: {kwargs['year']}-present {kwargs['author']} <{kwargs['email']}> +# SPDX-FileCopyrightText: {kwargs["year"]}-present {kwargs["author"]} <{kwargs["email"]}> # # SPDX-License-Identifier: Apache-2.0 OR MIT """, ), File( - Path('README.md'), + Path("README.md"), f"""\ -# {kwargs['project_name']} +# {kwargs["project_name"]} -[![PyPI - Version](https://img.shields.io/pypi/v/{kwargs['project_name_normalized']}.svg)](https://pypi.org/project/{kwargs['project_name_normalized']}) -[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/{kwargs['project_name_normalized']}.svg)](https://pypi.org/project/{kwargs['project_name_normalized']}) +[![PyPI - Version](https://img.shields.io/pypi/v/{kwargs["project_name_normalized"]}.svg)](https://pypi.org/project/{kwargs["project_name_normalized"]}) +[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/{kwargs["project_name_normalized"]}.svg)](https://pypi.org/project/{kwargs["project_name_normalized"]}) ----- @@ -56,26 +56,26 @@ def get_files(**kwargs): ## Installation ```console -pip install {kwargs['project_name_normalized']} +pip install {kwargs["project_name_normalized"]} ``` ## License -`{kwargs['project_name_normalized']}` is distributed under the terms of any of the following licenses: +`{kwargs["project_name_normalized"]}` is distributed under the terms of any of the following licenses: - [Apache-2.0](https://spdx.org/licenses/Apache-2.0.html) - [MIT](https://spdx.org/licenses/MIT.html) """, ), File( - Path('pyproject.toml'), + Path("pyproject.toml"), f"""\ [build-system] requires = ["hatchling"] build-backend = "hatchling.build" [project] -name = "{kwargs['project_name_normalized']}" +name = "{kwargs["project_name_normalized"]}" dynamic = ["version"] description = '' readme = "README.md" @@ -84,7 +84,7 @@ def get_files(**kwargs): license-files = {{ globs = ["LICENSES/*"] }} keywords = [] authors = [ - {{ name = "{kwargs['author']}", email = "{kwargs['email']}" }}, + {{ name = "{kwargs["author"]}", email = "{kwargs["email"]}" }}, ] classifiers = [ "Development Status :: 4 - Beta", @@ -100,31 +100,31 @@ def get_files(**kwargs): dependencies = [] [project.urls] -Documentation = "https://github.com/{kwargs['author']}/{kwargs['project_name_normalized']}#readme" -Issues = "https://github.com/{kwargs['author']}/{kwargs['project_name_normalized']}/issues" -Source = "https://github.com/{kwargs['author']}/{kwargs['project_name_normalized']}" +Documentation = "https://github.com/{kwargs["author"]}/{kwargs["project_name_normalized"]}#readme" +Issues = "https://github.com/{kwargs["author"]}/{kwargs["project_name_normalized"]}/issues" +Source = "https://github.com/{kwargs["author"]}/{kwargs["project_name_normalized"]}" [tool.hatch.version] -path = "src/{kwargs['package_name']}/__about__.py" +path = "src/{kwargs["package_name"]}/__about__.py" [tool.hatch.envs.types] extra-dependencies = [ "mypy>=1.0.0", ] [tool.hatch.envs.types.scripts] -check = "mypy --install-types --non-interactive {{args:src/{kwargs['package_name']} tests}}" +check = "mypy --install-types --non-interactive {{args:src/{kwargs["package_name"]} tests}}" [tool.coverage.run] -source_pkgs = ["{kwargs['package_name']}", "tests"] +source_pkgs = ["{kwargs["package_name"]}", "tests"] branch = true parallel = true omit = [ - "src/{kwargs['package_name']}/__about__.py", + "src/{kwargs["package_name"]}/__about__.py", ] [tool.coverage.paths] -{kwargs['package_name']} = ["src/{kwargs['package_name']}", "*/{kwargs['project_name_normalized']}/src/{kwargs['package_name']}"] -tests = ["tests", "*/{kwargs['project_name_normalized']}/tests"] +{kwargs["package_name"]} = ["src/{kwargs["package_name"]}", "*/{kwargs["project_name_normalized"]}/src/{kwargs["package_name"]}"] +tests = ["tests", "*/{kwargs["project_name_normalized"]}/tests"] [tool.coverage.report] exclude_lines = [ diff --git a/tests/helpers/templates/new/projects_urls_empty.py b/tests/helpers/templates/new/projects_urls_empty.py index 3b66ecf47..e48badc99 100644 --- a/tests/helpers/templates/new/projects_urls_empty.py +++ b/tests/helpers/templates/new/projects_urls_empty.py @@ -7,43 +7,43 @@ def get_files(**kwargs): return [ File( - Path('LICENSE.txt'), - MIT.replace('', f"{kwargs['year']}-present", 1).replace( - '', f"{kwargs['author']} <{kwargs['email']}>", 1 + Path("LICENSE.txt"), + MIT.replace("", f"{kwargs['year']}-present", 1).replace( + "", f"{kwargs['author']} <{kwargs['email']}>", 1 ), ), File( - Path('src', kwargs['package_name'], '__init__.py'), + Path("src", kwargs["package_name"], "__init__.py"), f"""\ -# SPDX-FileCopyrightText: {kwargs['year']}-present {kwargs['author']} <{kwargs['email']}> +# SPDX-FileCopyrightText: {kwargs["year"]}-present {kwargs["author"]} <{kwargs["email"]}> # # SPDX-License-Identifier: MIT """, ), File( - Path('src', kwargs['package_name'], '__about__.py'), + Path("src", kwargs["package_name"], "__about__.py"), f"""\ -# SPDX-FileCopyrightText: {kwargs['year']}-present {kwargs['author']} <{kwargs['email']}> +# SPDX-FileCopyrightText: {kwargs["year"]}-present {kwargs["author"]} <{kwargs["email"]}> # # SPDX-License-Identifier: MIT __version__ = "0.0.1" """, ), File( - Path('tests', '__init__.py'), + Path("tests", "__init__.py"), f"""\ -# SPDX-FileCopyrightText: {kwargs['year']}-present {kwargs['author']} <{kwargs['email']}> +# SPDX-FileCopyrightText: {kwargs["year"]}-present {kwargs["author"]} <{kwargs["email"]}> # # SPDX-License-Identifier: MIT """, ), File( - Path('README.md'), + Path("README.md"), f"""\ -# {kwargs['project_name']} +# {kwargs["project_name"]} -[![PyPI - Version](https://img.shields.io/pypi/v/{kwargs['project_name_normalized']}.svg)](https://pypi.org/project/{kwargs['project_name_normalized']}) -[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/{kwargs['project_name_normalized']}.svg)](https://pypi.org/project/{kwargs['project_name_normalized']}) +[![PyPI - Version](https://img.shields.io/pypi/v/{kwargs["project_name_normalized"]}.svg)](https://pypi.org/project/{kwargs["project_name_normalized"]}) +[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/{kwargs["project_name_normalized"]}.svg)](https://pypi.org/project/{kwargs["project_name_normalized"]}) ----- @@ -55,23 +55,23 @@ def get_files(**kwargs): ## Installation ```console -pip install {kwargs['project_name_normalized']} +pip install {kwargs["project_name_normalized"]} ``` ## License -`{kwargs['project_name_normalized']}` is distributed under the terms of the [MIT](https://spdx.org/licenses/MIT.html) license. +`{kwargs["project_name_normalized"]}` is distributed under the terms of the [MIT](https://spdx.org/licenses/MIT.html) license. """, ), File( - Path('pyproject.toml'), + Path("pyproject.toml"), f"""\ [build-system] requires = ["hatchling"] build-backend = "hatchling.build" [project] -name = "{kwargs['project_name_normalized']}" +name = "{kwargs["project_name_normalized"]}" dynamic = ["version"] description = '' readme = "README.md" @@ -79,7 +79,7 @@ def get_files(**kwargs): license = "MIT" keywords = [] authors = [ - {{ name = "{kwargs['author']}", email = "{kwargs['email']}" }}, + {{ name = "{kwargs["author"]}", email = "{kwargs["email"]}" }}, ] classifiers = [ "Development Status :: 4 - Beta", @@ -97,26 +97,26 @@ def get_files(**kwargs): [project.urls] [tool.hatch.version] -path = "src/{kwargs['package_name']}/__about__.py" +path = "src/{kwargs["package_name"]}/__about__.py" [tool.hatch.envs.types] extra-dependencies = [ "mypy>=1.0.0", ] [tool.hatch.envs.types.scripts] -check = "mypy --install-types --non-interactive {{args:src/{kwargs['package_name']} tests}}" +check = "mypy --install-types --non-interactive {{args:src/{kwargs["package_name"]} tests}}" [tool.coverage.run] -source_pkgs = ["{kwargs['package_name']}", "tests"] +source_pkgs = ["{kwargs["package_name"]}", "tests"] branch = true parallel = true omit = [ - "src/{kwargs['package_name']}/__about__.py", + "src/{kwargs["package_name"]}/__about__.py", ] [tool.coverage.paths] -{kwargs['package_name']} = ["src/{kwargs['package_name']}", "*/{kwargs['project_name_normalized']}/src/{kwargs['package_name']}"] -tests = ["tests", "*/{kwargs['project_name_normalized']}/tests"] +{kwargs["package_name"]} = ["src/{kwargs["package_name"]}", "*/{kwargs["project_name_normalized"]}/src/{kwargs["package_name"]}"] +tests = ["tests", "*/{kwargs["project_name_normalized"]}/tests"] [tool.coverage.report] exclude_lines = [ diff --git a/tests/helpers/templates/new/projects_urls_space_in_label.py b/tests/helpers/templates/new/projects_urls_space_in_label.py index e4630520a..fa128ba58 100644 --- a/tests/helpers/templates/new/projects_urls_space_in_label.py +++ b/tests/helpers/templates/new/projects_urls_space_in_label.py @@ -7,43 +7,43 @@ def get_files(**kwargs): return [ File( - Path('LICENSE.txt'), - MIT.replace('', f"{kwargs['year']}-present", 1).replace( - '', f"{kwargs['author']} <{kwargs['email']}>", 1 + Path("LICENSE.txt"), + MIT.replace("", f"{kwargs['year']}-present", 1).replace( + "", f"{kwargs['author']} <{kwargs['email']}>", 1 ), ), File( - Path('src', kwargs['package_name'], '__init__.py'), + Path("src", kwargs["package_name"], "__init__.py"), f"""\ -# SPDX-FileCopyrightText: {kwargs['year']}-present {kwargs['author']} <{kwargs['email']}> +# SPDX-FileCopyrightText: {kwargs["year"]}-present {kwargs["author"]} <{kwargs["email"]}> # # SPDX-License-Identifier: MIT """, ), File( - Path('src', kwargs['package_name'], '__about__.py'), + Path("src", kwargs["package_name"], "__about__.py"), f"""\ -# SPDX-FileCopyrightText: {kwargs['year']}-present {kwargs['author']} <{kwargs['email']}> +# SPDX-FileCopyrightText: {kwargs["year"]}-present {kwargs["author"]} <{kwargs["email"]}> # # SPDX-License-Identifier: MIT __version__ = "0.0.1" """, ), File( - Path('tests', '__init__.py'), + Path("tests", "__init__.py"), f"""\ -# SPDX-FileCopyrightText: {kwargs['year']}-present {kwargs['author']} <{kwargs['email']}> +# SPDX-FileCopyrightText: {kwargs["year"]}-present {kwargs["author"]} <{kwargs["email"]}> # # SPDX-License-Identifier: MIT """, ), File( - Path('README.md'), + Path("README.md"), f"""\ -# {kwargs['project_name']} +# {kwargs["project_name"]} -[![PyPI - Version](https://img.shields.io/pypi/v/{kwargs['project_name_normalized']}.svg)](https://pypi.org/project/{kwargs['project_name_normalized']}) -[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/{kwargs['project_name_normalized']}.svg)](https://pypi.org/project/{kwargs['project_name_normalized']}) +[![PyPI - Version](https://img.shields.io/pypi/v/{kwargs["project_name_normalized"]}.svg)](https://pypi.org/project/{kwargs["project_name_normalized"]}) +[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/{kwargs["project_name_normalized"]}.svg)](https://pypi.org/project/{kwargs["project_name_normalized"]}) ----- @@ -55,23 +55,23 @@ def get_files(**kwargs): ## Installation ```console -pip install {kwargs['project_name_normalized']} +pip install {kwargs["project_name_normalized"]} ``` ## License -`{kwargs['project_name_normalized']}` is distributed under the terms of the [MIT](https://spdx.org/licenses/MIT.html) license. +`{kwargs["project_name_normalized"]}` is distributed under the terms of the [MIT](https://spdx.org/licenses/MIT.html) license. """, ), File( - Path('pyproject.toml'), + Path("pyproject.toml"), f"""\ [build-system] requires = ["hatchling"] build-backend = "hatchling.build" [project] -name = "{kwargs['project_name_normalized']}" +name = "{kwargs["project_name_normalized"]}" dynamic = ["version"] description = '' readme = "README.md" @@ -79,7 +79,7 @@ def get_files(**kwargs): license = "MIT" keywords = [] authors = [ - {{ name = "{kwargs['author']}", email = "{kwargs['email']}" }}, + {{ name = "{kwargs["author"]}", email = "{kwargs["email"]}" }}, ] classifiers = [ "Development Status :: 4 - Beta", @@ -95,31 +95,31 @@ def get_files(**kwargs): dependencies = [] [project.urls] -Documentation = "https://github.com/{kwargs['author']}/{kwargs['project_name_normalized']}#readme" -Source = "https://github.com/{kwargs['author']}/{kwargs['project_name_normalized']}" -"Bug Tracker" = "https://github.com/{kwargs['author']}/{kwargs['project_name_normalized']}/issues" +Documentation = "https://github.com/{kwargs["author"]}/{kwargs["project_name_normalized"]}#readme" +Source = "https://github.com/{kwargs["author"]}/{kwargs["project_name_normalized"]}" +"Bug Tracker" = "https://github.com/{kwargs["author"]}/{kwargs["project_name_normalized"]}/issues" [tool.hatch.version] -path = "src/{kwargs['package_name']}/__about__.py" +path = "src/{kwargs["package_name"]}/__about__.py" [tool.hatch.envs.types] extra-dependencies = [ "mypy>=1.0.0", ] [tool.hatch.envs.types.scripts] -check = "mypy --install-types --non-interactive {{args:src/{kwargs['package_name']} tests}}" +check = "mypy --install-types --non-interactive {{args:src/{kwargs["package_name"]} tests}}" [tool.coverage.run] -source_pkgs = ["{kwargs['package_name']}", "tests"] +source_pkgs = ["{kwargs["package_name"]}", "tests"] branch = true parallel = true omit = [ - "src/{kwargs['package_name']}/__about__.py", + "src/{kwargs["package_name"]}/__about__.py", ] [tool.coverage.paths] -{kwargs['package_name']} = ["src/{kwargs['package_name']}", "*/{kwargs['project_name_normalized']}/src/{kwargs['package_name']}"] -tests = ["tests", "*/{kwargs['project_name_normalized']}/tests"] +{kwargs["package_name"]} = ["src/{kwargs["package_name"]}", "*/{kwargs["project_name_normalized"]}/src/{kwargs["package_name"]}"] +tests = ["tests", "*/{kwargs["project_name_normalized"]}/tests"] [tool.coverage.report] exclude_lines = [ diff --git a/tests/helpers/templates/sdist/standard_default.py b/tests/helpers/templates/sdist/standard_default.py index 301275412..0127eff6f 100644 --- a/tests/helpers/templates/sdist/standard_default.py +++ b/tests/helpers/templates/sdist/standard_default.py @@ -6,15 +6,15 @@ def get_files(**kwargs): - relative_root = kwargs.get('relative_root', '') + relative_root = kwargs.get("relative_root", "") files = [File(Path(relative_root, f.path), f.contents) for f in get_template_files(**kwargs)] files.append( File( - Path(relative_root, 'PKG-INFO'), + Path(relative_root, "PKG-INFO"), f"""\ Metadata-Version: {DEFAULT_METADATA_VERSION} -Name: {kwargs['project_name']} +Name: {kwargs["project_name"]} Version: 0.0.1 License-File: LICENSE.txt """, diff --git a/tests/helpers/templates/sdist/standard_default_build_script_artifacts.py b/tests/helpers/templates/sdist/standard_default_build_script_artifacts.py index 29d2a4b25..4447422be 100644 --- a/tests/helpers/templates/sdist/standard_default_build_script_artifacts.py +++ b/tests/helpers/templates/sdist/standard_default_build_script_artifacts.py @@ -7,13 +7,13 @@ def get_files(**kwargs): - relative_root = kwargs.get('relative_root', '') + relative_root = kwargs.get("relative_root", "") files = [File(Path(relative_root, f.path), f.contents) for f in get_template_files(**kwargs)] files.extend(( - File(Path(relative_root, kwargs['package_name'], 'lib.so'), ''), + File(Path(relative_root, kwargs["package_name"], "lib.so"), ""), File( - Path(relative_root, '.gitignore'), + Path(relative_root, ".gitignore"), """\ *.pyc *.so @@ -34,10 +34,10 @@ def initialize(self, version, build_data): """, ), File( - Path(relative_root, 'PKG-INFO'), + Path(relative_root, "PKG-INFO"), f"""\ Metadata-Version: {DEFAULT_METADATA_VERSION} -Name: {kwargs['project_name']} +Name: {kwargs["project_name"]} Version: 0.0.1 License-File: LICENSE.txt """, diff --git a/tests/helpers/templates/sdist/standard_default_build_script_extra_dependencies.py b/tests/helpers/templates/sdist/standard_default_build_script_extra_dependencies.py index eb6c0b14b..981d5ca14 100644 --- a/tests/helpers/templates/sdist/standard_default_build_script_extra_dependencies.py +++ b/tests/helpers/templates/sdist/standard_default_build_script_extra_dependencies.py @@ -7,13 +7,13 @@ def get_files(**kwargs): - relative_root = kwargs.get('relative_root', '') + relative_root = kwargs.get("relative_root", "") files = [File(Path(relative_root, f.path), f.contents) for f in get_template_files(**kwargs)] files.extend(( - File(Path(relative_root, kwargs['package_name'], 'lib.so'), ''), + File(Path(relative_root, kwargs["package_name"], "lib.so"), ""), File( - Path(relative_root, '.gitignore'), + Path(relative_root, ".gitignore"), """\ *.pyc *.so @@ -35,10 +35,10 @@ def initialize(self, version, build_data): """, ), File( - Path(relative_root, 'PKG-INFO'), + Path(relative_root, "PKG-INFO"), f"""\ Metadata-Version: {DEFAULT_METADATA_VERSION} -Name: {kwargs['project_name']} +Name: {kwargs["project_name"]} Version: 0.0.1 License-File: LICENSE.txt Requires-Dist: binary diff --git a/tests/helpers/templates/sdist/standard_default_support_legacy.py b/tests/helpers/templates/sdist/standard_default_support_legacy.py index 05b020fd8..cecaaf8a4 100644 --- a/tests/helpers/templates/sdist/standard_default_support_legacy.py +++ b/tests/helpers/templates/sdist/standard_default_support_legacy.py @@ -6,29 +6,29 @@ def get_files(**kwargs): - relative_root = kwargs.get('relative_root', '') + relative_root = kwargs.get("relative_root", "") files = [File(Path(relative_root, f.path), f.contents) for f in get_template_files(**kwargs)] files.extend(( File( - Path(relative_root, 'PKG-INFO'), + Path(relative_root, "PKG-INFO"), f"""\ Metadata-Version: {DEFAULT_METADATA_VERSION} -Name: {kwargs['project_name']} +Name: {kwargs["project_name"]} Version: 0.0.1 License-File: LICENSE.txt """, ), File( - Path(relative_root, 'setup.py'), + Path(relative_root, "setup.py"), f"""\ from setuptools import setup setup( - name='{kwargs['project_name_normalized']}', + name='{kwargs["project_name_normalized"]}', version='0.0.1', packages=[ - '{kwargs['package_name']}', + '{kwargs["package_name"]}', 'tests', ], ) diff --git a/tests/helpers/templates/sdist/standard_default_vcs_git_exclusion_files.py b/tests/helpers/templates/sdist/standard_default_vcs_git_exclusion_files.py index 09a8cebb1..f1a334317 100644 --- a/tests/helpers/templates/sdist/standard_default_vcs_git_exclusion_files.py +++ b/tests/helpers/templates/sdist/standard_default_vcs_git_exclusion_files.py @@ -6,13 +6,13 @@ def get_files(**kwargs): - relative_root = kwargs.get('relative_root', '') + relative_root = kwargs.get("relative_root", "") files = [File(Path(relative_root, f.path), f.contents) for f in get_template_files(**kwargs)] files.extend(( - File(Path(relative_root, kwargs['package_name'], 'lib.so'), ''), + File(Path(relative_root, kwargs["package_name"], "lib.so"), ""), File( - Path(relative_root, '.gitignore'), + Path(relative_root, ".gitignore"), """\ *.pyc *.so @@ -20,10 +20,10 @@ def get_files(**kwargs): """, ), File( - Path(relative_root, 'PKG-INFO'), + Path(relative_root, "PKG-INFO"), f"""\ Metadata-Version: {DEFAULT_METADATA_VERSION} -Name: {kwargs['project_name']} +Name: {kwargs["project_name"]} Version: 0.0.1 License-File: LICENSE.txt """, diff --git a/tests/helpers/templates/sdist/standard_default_vcs_mercurial_exclusion_files.py b/tests/helpers/templates/sdist/standard_default_vcs_mercurial_exclusion_files.py index f8cc3d596..1b7c7359c 100644 --- a/tests/helpers/templates/sdist/standard_default_vcs_mercurial_exclusion_files.py +++ b/tests/helpers/templates/sdist/standard_default_vcs_mercurial_exclusion_files.py @@ -6,13 +6,13 @@ def get_files(**kwargs): - relative_root = kwargs.get('relative_root', '') + relative_root = kwargs.get("relative_root", "") files = [File(Path(relative_root, f.path), f.contents) for f in get_template_files(**kwargs)] files.extend(( - File(Path(relative_root, kwargs['package_name'], 'lib.so'), ''), + File(Path(relative_root, kwargs["package_name"], "lib.so"), ""), File( - Path(relative_root, '.hgignore'), + Path(relative_root, ".hgignore"), """\ syntax: glob *.pyc @@ -26,10 +26,10 @@ def get_files(**kwargs): """, ), File( - Path(relative_root, 'PKG-INFO'), + Path(relative_root, "PKG-INFO"), f"""\ Metadata-Version: {DEFAULT_METADATA_VERSION} -Name: {kwargs['project_name']} +Name: {kwargs["project_name"]} Version: 0.0.1 License-File: LICENSE.txt """, diff --git a/tests/helpers/templates/sdist/standard_include.py b/tests/helpers/templates/sdist/standard_include.py index 96abbe7e4..128b3151d 100644 --- a/tests/helpers/templates/sdist/standard_include.py +++ b/tests/helpers/templates/sdist/standard_include.py @@ -6,20 +6,20 @@ def get_files(**kwargs): - relative_root = kwargs.get('relative_root', '') + relative_root = kwargs.get("relative_root", "") files = [] for f in get_template_files(**kwargs): part = f.path.parts[0] - if part in {'my_app', 'pyproject.toml', 'README.md', 'LICENSE.txt'}: + if part in {"my_app", "pyproject.toml", "README.md", "LICENSE.txt"}: files.append(File(Path(relative_root, f.path), f.contents)) files.append( File( - Path(relative_root, 'PKG-INFO'), + Path(relative_root, "PKG-INFO"), f"""\ Metadata-Version: {DEFAULT_METADATA_VERSION} -Name: {kwargs['project_name']} +Name: {kwargs["project_name"]} Version: 0.0.1 License-File: LICENSE.txt Description-Content-Type: text/markdown diff --git a/tests/helpers/templates/sdist/standard_include_config_file.py b/tests/helpers/templates/sdist/standard_include_config_file.py index 27272aee0..8345d7134 100644 --- a/tests/helpers/templates/sdist/standard_include_config_file.py +++ b/tests/helpers/templates/sdist/standard_include_config_file.py @@ -6,21 +6,21 @@ def get_files(**kwargs): - relative_root = kwargs.get('relative_root', '') + relative_root = kwargs.get("relative_root", "") files = [] for f in get_template_files(**kwargs): part = f.path.parts[0] - if part in {'my_app', 'pyproject.toml', 'README.md', 'LICENSE.txt'}: + if part in {"my_app", "pyproject.toml", "README.md", "LICENSE.txt"}: files.append(File(Path(relative_root, f.path), f.contents)) files.extend(( - File(Path(relative_root, 'hatch.toml'), ''), + File(Path(relative_root, "hatch.toml"), ""), File( - Path(relative_root, 'PKG-INFO'), + Path(relative_root, "PKG-INFO"), f"""\ Metadata-Version: {DEFAULT_METADATA_VERSION} -Name: {kwargs['project_name']} +Name: {kwargs["project_name"]} Version: 0.0.1 License-File: LICENSE.txt Description-Content-Type: text/markdown diff --git a/tests/helpers/templates/wheel/standard_default_build_script.py b/tests/helpers/templates/wheel/standard_default_build_script.py index 69d3ceca0..d7018e88c 100644 --- a/tests/helpers/templates/wheel/standard_default_build_script.py +++ b/tests/helpers/templates/wheel/standard_default_build_script.py @@ -8,33 +8,33 @@ def get_files(**kwargs): - metadata_directory = kwargs.get('metadata_directory', '') + metadata_directory = kwargs.get("metadata_directory", "") files = [] for f in get_template_files(**kwargs): - if str(f.path) == 'LICENSE.txt': - files.append(File(Path(metadata_directory, 'licenses', f.path), f.contents)) + if str(f.path) == "LICENSE.txt": + files.append(File(Path(metadata_directory, "licenses", f.path), f.contents)) - if f.path.parts[0] != kwargs['package_name']: + if f.path.parts[0] != kwargs["package_name"]: continue files.append(f) files.extend(( File( - Path(metadata_directory, 'WHEEL'), + Path(metadata_directory, "WHEEL"), f"""\ Wheel-Version: 1.0 Generator: hatchling {__version__} Root-Is-Purelib: true -Tag: {kwargs.get('tag', '')} +Tag: {kwargs.get("tag", "")} """, ), File( - Path(metadata_directory, 'METADATA'), + Path(metadata_directory, "METADATA"), f"""\ Metadata-Version: {DEFAULT_METADATA_VERSION} -Name: {kwargs['project_name']} +Name: {kwargs["project_name"]} Version: 0.0.1 License-File: LICENSE.txt Requires-Python: >3 @@ -42,7 +42,7 @@ def get_files(**kwargs): ), )) - record_file = File(Path(metadata_directory, 'RECORD'), '') + record_file = File(Path(metadata_directory, "RECORD"), "") update_record_file_contents(record_file, files) files.append(record_file) diff --git a/tests/helpers/templates/wheel/standard_default_build_script_artifacts.py b/tests/helpers/templates/wheel/standard_default_build_script_artifacts.py index 19d0e58fb..7d3240213 100644 --- a/tests/helpers/templates/wheel/standard_default_build_script_artifacts.py +++ b/tests/helpers/templates/wheel/standard_default_build_script_artifacts.py @@ -8,34 +8,34 @@ def get_files(**kwargs): - metadata_directory = kwargs.get('metadata_directory', '') + metadata_directory = kwargs.get("metadata_directory", "") files = [] for f in get_template_files(**kwargs): - if str(f.path) == 'LICENSE.txt': - files.append(File(Path(metadata_directory, 'licenses', f.path), f.contents)) + if str(f.path) == "LICENSE.txt": + files.append(File(Path(metadata_directory, "licenses", f.path), f.contents)) - if f.path.parts[0] != kwargs['package_name']: + if f.path.parts[0] != kwargs["package_name"]: continue files.append(f) files.extend(( - File(Path(kwargs['package_name'], 'lib.so'), ''), + File(Path(kwargs["package_name"], "lib.so"), ""), File( - Path(metadata_directory, 'WHEEL'), + Path(metadata_directory, "WHEEL"), f"""\ Wheel-Version: 1.0 Generator: hatchling {__version__} Root-Is-Purelib: false -Tag: {kwargs.get('tag', '')} +Tag: {kwargs.get("tag", "")} """, ), File( - Path(metadata_directory, 'METADATA'), + Path(metadata_directory, "METADATA"), f"""\ Metadata-Version: {DEFAULT_METADATA_VERSION} -Name: {kwargs['project_name']} +Name: {kwargs["project_name"]} Version: 0.0.1 License-File: LICENSE.txt Requires-Python: >3 @@ -43,7 +43,7 @@ def get_files(**kwargs): ), )) - record_file = File(Path(metadata_directory, 'RECORD'), '') + record_file = File(Path(metadata_directory, "RECORD"), "") update_record_file_contents(record_file, files) files.append(record_file) diff --git a/tests/helpers/templates/wheel/standard_default_build_script_artifacts_with_src_layout.py b/tests/helpers/templates/wheel/standard_default_build_script_artifacts_with_src_layout.py index ac1cc84d8..4c528f2fc 100644 --- a/tests/helpers/templates/wheel/standard_default_build_script_artifacts_with_src_layout.py +++ b/tests/helpers/templates/wheel/standard_default_build_script_artifacts_with_src_layout.py @@ -8,35 +8,35 @@ def get_files(**kwargs): - metadata_directory = kwargs.get('metadata_directory', '') + metadata_directory = kwargs.get("metadata_directory", "") files = [] for f in get_template_files(**kwargs): - if str(f.path) == 'LICENSE.txt': - files.append(File(Path(metadata_directory, 'licenses', f.path), f.contents)) + if str(f.path) == "LICENSE.txt": + files.append(File(Path(metadata_directory, "licenses", f.path), f.contents)) - if f.path.parts[0] != 'src': + if f.path.parts[0] != "src": continue files.append(File(Path(*f.path.parts[1:]), f.contents)) files.extend(( - File(Path(kwargs['package_name'], 'lib.so'), ''), - File(Path('zlib.pyd'), ''), + File(Path(kwargs["package_name"], "lib.so"), ""), + File(Path("zlib.pyd"), ""), File( - Path(metadata_directory, 'WHEEL'), + Path(metadata_directory, "WHEEL"), f"""\ Wheel-Version: 1.0 Generator: hatchling {__version__} Root-Is-Purelib: false -Tag: {kwargs.get('tag', '')} +Tag: {kwargs.get("tag", "")} """, ), File( - Path(metadata_directory, 'METADATA'), + Path(metadata_directory, "METADATA"), f"""\ Metadata-Version: {DEFAULT_METADATA_VERSION} -Name: {kwargs['project_name']} +Name: {kwargs["project_name"]} Version: 0.0.1 License-File: LICENSE.txt Requires-Python: >3 @@ -44,7 +44,7 @@ def get_files(**kwargs): ), )) - record_file = File(Path(metadata_directory, 'RECORD'), '') + record_file = File(Path(metadata_directory, "RECORD"), "") update_record_file_contents(record_file, files) files.append(record_file) diff --git a/tests/helpers/templates/wheel/standard_default_build_script_configured_build_hooks.py b/tests/helpers/templates/wheel/standard_default_build_script_configured_build_hooks.py index 31e34ed6d..5031bfbd0 100644 --- a/tests/helpers/templates/wheel/standard_default_build_script_configured_build_hooks.py +++ b/tests/helpers/templates/wheel/standard_default_build_script_configured_build_hooks.py @@ -8,34 +8,34 @@ def get_files(**kwargs): - metadata_directory = kwargs.get('metadata_directory', '') + metadata_directory = kwargs.get("metadata_directory", "") files = [] for f in get_template_files(**kwargs): - if str(f.path) == 'LICENSE.txt': - files.append(File(Path(metadata_directory, 'licenses', f.path), f.contents)) + if str(f.path) == "LICENSE.txt": + files.append(File(Path(metadata_directory, "licenses", f.path), f.contents)) - if f.path.parts[0] != kwargs['package_name']: + if f.path.parts[0] != kwargs["package_name"]: continue files.append(f) files.extend(( - File(Path(kwargs['package_name'], 'lib.so'), 'custom'), + File(Path(kwargs["package_name"], "lib.so"), "custom"), File( - Path(metadata_directory, 'WHEEL'), + Path(metadata_directory, "WHEEL"), f"""\ Wheel-Version: 1.0 Generator: hatchling {__version__} Root-Is-Purelib: false -Tag: {kwargs.get('tag', '')} +Tag: {kwargs.get("tag", "")} """, ), File( - Path(metadata_directory, 'METADATA'), + Path(metadata_directory, "METADATA"), f"""\ Metadata-Version: {DEFAULT_METADATA_VERSION} -Name: {kwargs['project_name']} +Name: {kwargs["project_name"]} Version: 0.0.1 License-File: LICENSE.txt Requires-Python: >3 @@ -43,7 +43,7 @@ def get_files(**kwargs): ), )) - record_file = File(Path(metadata_directory, 'RECORD'), '') + record_file = File(Path(metadata_directory, "RECORD"), "") update_record_file_contents(record_file, files) files.append(record_file) diff --git a/tests/helpers/templates/wheel/standard_default_build_script_extra_dependencies.py b/tests/helpers/templates/wheel/standard_default_build_script_extra_dependencies.py index 13d2c95b9..f08395864 100644 --- a/tests/helpers/templates/wheel/standard_default_build_script_extra_dependencies.py +++ b/tests/helpers/templates/wheel/standard_default_build_script_extra_dependencies.py @@ -8,34 +8,34 @@ def get_files(**kwargs): - metadata_directory = kwargs.get('metadata_directory', '') + metadata_directory = kwargs.get("metadata_directory", "") files = [] for f in get_template_files(**kwargs): - if str(f.path) == 'LICENSE.txt': - files.append(File(Path(metadata_directory, 'licenses', f.path), f.contents)) + if str(f.path) == "LICENSE.txt": + files.append(File(Path(metadata_directory, "licenses", f.path), f.contents)) - if f.path.parts[0] != kwargs['package_name']: + if f.path.parts[0] != kwargs["package_name"]: continue files.append(f) files.extend(( - File(Path(kwargs['package_name'], 'lib.so'), ''), + File(Path(kwargs["package_name"], "lib.so"), ""), File( - Path(metadata_directory, 'WHEEL'), + Path(metadata_directory, "WHEEL"), f"""\ Wheel-Version: 1.0 Generator: hatchling {__version__} Root-Is-Purelib: false -Tag: {kwargs.get('tag', '')} +Tag: {kwargs.get("tag", "")} """, ), File( - Path(metadata_directory, 'METADATA'), + Path(metadata_directory, "METADATA"), f"""\ Metadata-Version: {DEFAULT_METADATA_VERSION} -Name: {kwargs['project_name']} +Name: {kwargs["project_name"]} Version: 0.0.1 License-File: LICENSE.txt Requires-Python: >3 @@ -44,7 +44,7 @@ def get_files(**kwargs): ), )) - record_file = File(Path(metadata_directory, 'RECORD'), '') + record_file = File(Path(metadata_directory, "RECORD"), "") update_record_file_contents(record_file, files) files.append(record_file) diff --git a/tests/helpers/templates/wheel/standard_default_build_script_force_include.py b/tests/helpers/templates/wheel/standard_default_build_script_force_include.py index d32daf6be..7f762f82e 100644 --- a/tests/helpers/templates/wheel/standard_default_build_script_force_include.py +++ b/tests/helpers/templates/wheel/standard_default_build_script_force_include.py @@ -8,35 +8,35 @@ def get_files(**kwargs): - metadata_directory = kwargs.get('metadata_directory', '') + metadata_directory = kwargs.get("metadata_directory", "") files = [] for f in get_template_files(**kwargs): - if str(f.path) == 'LICENSE.txt': - files.append(File(Path(metadata_directory, 'licenses', f.path), f.contents)) + if str(f.path) == "LICENSE.txt": + files.append(File(Path(metadata_directory, "licenses", f.path), f.contents)) - if f.path.parts[0] != kwargs['package_name']: + if f.path.parts[0] != kwargs["package_name"]: continue files.append(f) files.extend(( - File(Path(kwargs['package_name'], 'lib.so'), ''), - File(Path(kwargs['package_name'], 'lib.h'), ''), + File(Path(kwargs["package_name"], "lib.so"), ""), + File(Path(kwargs["package_name"], "lib.h"), ""), File( - Path(metadata_directory, 'WHEEL'), + Path(metadata_directory, "WHEEL"), f"""\ Wheel-Version: 1.0 Generator: hatchling {__version__} Root-Is-Purelib: false -Tag: {kwargs.get('tag', '')} +Tag: {kwargs.get("tag", "")} """, ), File( - Path(metadata_directory, 'METADATA'), + Path(metadata_directory, "METADATA"), f"""\ Metadata-Version: {DEFAULT_METADATA_VERSION} -Name: {kwargs['project_name']} +Name: {kwargs["project_name"]} Version: 0.0.1 License-File: LICENSE.txt Requires-Python: >3 @@ -44,7 +44,7 @@ def get_files(**kwargs): ), )) - record_file = File(Path(metadata_directory, 'RECORD'), '') + record_file = File(Path(metadata_directory, "RECORD"), "") update_record_file_contents(record_file, files) files.append(record_file) diff --git a/tests/helpers/templates/wheel/standard_default_build_script_force_include_no_duplication.py b/tests/helpers/templates/wheel/standard_default_build_script_force_include_no_duplication.py index 2ec64023d..9de705f4a 100644 --- a/tests/helpers/templates/wheel/standard_default_build_script_force_include_no_duplication.py +++ b/tests/helpers/templates/wheel/standard_default_build_script_force_include_no_duplication.py @@ -8,34 +8,34 @@ def get_files(**kwargs): - metadata_directory = kwargs.get('metadata_directory', '') + metadata_directory = kwargs.get("metadata_directory", "") files = [] for f in get_template_files(**kwargs): - if str(f.path) == 'LICENSE.txt': - files.append(File(Path(metadata_directory, 'licenses', f.path), f.contents)) + if str(f.path) == "LICENSE.txt": + files.append(File(Path(metadata_directory, "licenses", f.path), f.contents)) - if f.path.parts[0] != kwargs['package_name']: + if f.path.parts[0] != kwargs["package_name"]: continue files.append(f) files.extend(( - File(Path(kwargs['package_name'], 'z.py'), 'print("hello world")'), + File(Path(kwargs["package_name"], "z.py"), 'print("hello world")'), File( - Path(metadata_directory, 'WHEEL'), + Path(metadata_directory, "WHEEL"), f"""\ Wheel-Version: 1.0 Generator: hatchling {__version__} Root-Is-Purelib: false -Tag: {kwargs.get('tag', '')} +Tag: {kwargs.get("tag", "")} """, ), File( - Path(metadata_directory, 'METADATA'), + Path(metadata_directory, "METADATA"), f"""\ Metadata-Version: {DEFAULT_METADATA_VERSION} -Name: {kwargs['project_name']} +Name: {kwargs["project_name"]} Version: 0.0.1 License-File: LICENSE.txt Requires-Python: >3 @@ -43,7 +43,7 @@ def get_files(**kwargs): ), )) - record_file = File(Path(metadata_directory, 'RECORD'), '') + record_file = File(Path(metadata_directory, "RECORD"), "") update_record_file_contents(record_file, files) files.append(record_file) diff --git a/tests/helpers/templates/wheel/standard_default_extra_metadata.py b/tests/helpers/templates/wheel/standard_default_extra_metadata.py index 614d09ff6..f390a377a 100644 --- a/tests/helpers/templates/wheel/standard_default_extra_metadata.py +++ b/tests/helpers/templates/wheel/standard_default_extra_metadata.py @@ -8,23 +8,23 @@ def get_files(**kwargs): - metadata_directory = kwargs.get('metadata_directory', '') + metadata_directory = kwargs.get("metadata_directory", "") files = [] for f in get_template_files(**kwargs): - if str(f.path) == 'LICENSE.txt': - files.append(File(Path(metadata_directory, 'licenses', f.path), f.contents)) + if str(f.path) == "LICENSE.txt": + files.append(File(Path(metadata_directory, "licenses", f.path), f.contents)) - if f.path.parts[0] != kwargs['package_name']: + if f.path.parts[0] != kwargs["package_name"]: continue files.append(f) files.extend(( - File(Path(metadata_directory, 'extra_metadata', 'foo.txt'), ''), - File(Path(metadata_directory, 'extra_metadata', 'nested', 'bar.txt'), ''), + File(Path(metadata_directory, "extra_metadata", "foo.txt"), ""), + File(Path(metadata_directory, "extra_metadata", "nested", "bar.txt"), ""), File( - Path(metadata_directory, 'WHEEL'), + Path(metadata_directory, "WHEEL"), f"""\ Wheel-Version: 1.0 Generator: hatchling {__version__} @@ -33,10 +33,10 @@ def get_files(**kwargs): """, ), File( - Path(metadata_directory, 'METADATA'), + Path(metadata_directory, "METADATA"), f"""\ Metadata-Version: {DEFAULT_METADATA_VERSION} -Name: {kwargs['project_name']} +Name: {kwargs["project_name"]} Version: 0.0.1 License-File: LICENSE.txt Requires-Python: >3 @@ -44,7 +44,7 @@ def get_files(**kwargs): ), )) - record_file = File(Path(metadata_directory, 'RECORD'), '') + record_file = File(Path(metadata_directory, "RECORD"), "") update_record_file_contents(record_file, files) files.append(record_file) diff --git a/tests/helpers/templates/wheel/standard_default_license_multiple.py b/tests/helpers/templates/wheel/standard_default_license_multiple.py index 7a330a70c..87d972310 100644 --- a/tests/helpers/templates/wheel/standard_default_license_multiple.py +++ b/tests/helpers/templates/wheel/standard_default_license_multiple.py @@ -8,23 +8,23 @@ def get_files(**kwargs): - metadata_directory = kwargs.get('metadata_directory', '') + metadata_directory = kwargs.get("metadata_directory", "") files = [] for f in get_template_files(**kwargs): first_part = f.path.parts[0] - if first_part == 'LICENSES': - files.append(File(Path(metadata_directory, 'licenses', 'LICENSES', f.path.parts[1]), f.contents)) + if first_part == "LICENSES": + files.append(File(Path(metadata_directory, "licenses", "LICENSES", f.path.parts[1]), f.contents)) - if f.path.parts[0] != 'src': + if f.path.parts[0] != "src": continue files.append(File(Path(*f.path.parts[1:]), f.contents)) files.extend(( File( - Path(metadata_directory, 'WHEEL'), + Path(metadata_directory, "WHEEL"), f"""\ Wheel-Version: 1.0 Generator: hatchling {__version__} @@ -34,10 +34,10 @@ def get_files(**kwargs): """, ), File( - Path(metadata_directory, 'METADATA'), + Path(metadata_directory, "METADATA"), f"""\ Metadata-Version: {DEFAULT_METADATA_VERSION} -Name: {kwargs['project_name']} +Name: {kwargs["project_name"]} Version: 0.0.1 License-File: LICENSES/Apache-2.0.txt License-File: LICENSES/MIT.txt @@ -45,7 +45,7 @@ def get_files(**kwargs): ), )) - record_file = File(Path(metadata_directory, 'RECORD'), '') + record_file = File(Path(metadata_directory, "RECORD"), "") update_record_file_contents(record_file, files) files.append(record_file) diff --git a/tests/helpers/templates/wheel/standard_default_license_single.py b/tests/helpers/templates/wheel/standard_default_license_single.py index 066d8e286..f961027e2 100644 --- a/tests/helpers/templates/wheel/standard_default_license_single.py +++ b/tests/helpers/templates/wheel/standard_default_license_single.py @@ -8,21 +8,21 @@ def get_files(**kwargs): - metadata_directory = kwargs.get('metadata_directory', '') + metadata_directory = kwargs.get("metadata_directory", "") files = [] for f in get_template_files(**kwargs): - if str(f.path) == 'LICENSE.txt': - files.append(File(Path(metadata_directory, 'licenses', f.path), f.contents)) + if str(f.path) == "LICENSE.txt": + files.append(File(Path(metadata_directory, "licenses", f.path), f.contents)) - if f.path.parts[0] != 'src': + if f.path.parts[0] != "src": continue files.append(File(Path(*f.path.parts[1:]), f.contents)) files.extend(( File( - Path(metadata_directory, 'WHEEL'), + Path(metadata_directory, "WHEEL"), f"""\ Wheel-Version: 1.0 Generator: hatchling {__version__} @@ -32,17 +32,17 @@ def get_files(**kwargs): """, ), File( - Path(metadata_directory, 'METADATA'), + Path(metadata_directory, "METADATA"), f"""\ Metadata-Version: {DEFAULT_METADATA_VERSION} -Name: {kwargs['project_name']} +Name: {kwargs["project_name"]} Version: 0.0.1 License-File: LICENSE.txt """, ), )) - record_file = File(Path(metadata_directory, 'RECORD'), '') + record_file = File(Path(metadata_directory, "RECORD"), "") update_record_file_contents(record_file, files) files.append(record_file) diff --git a/tests/helpers/templates/wheel/standard_default_namespace_package.py b/tests/helpers/templates/wheel/standard_default_namespace_package.py index 331fe4011..a1335db92 100644 --- a/tests/helpers/templates/wheel/standard_default_namespace_package.py +++ b/tests/helpers/templates/wheel/standard_default_namespace_package.py @@ -8,15 +8,15 @@ def get_files(**kwargs): - metadata_directory = kwargs.get('metadata_directory', '') - namespace_package = kwargs['namespace'] + metadata_directory = kwargs.get("metadata_directory", "") + namespace_package = kwargs["namespace"] files = [] for f in get_template_files(**kwargs): - if str(f.path) == 'LICENSE.txt': - files.append(File(Path(metadata_directory, 'licenses', f.path), f.contents)) + if str(f.path) == "LICENSE.txt": + files.append(File(Path(metadata_directory, "licenses", f.path), f.contents)) - if f.path.parts[0] != kwargs['package_name']: + if f.path.parts[0] != kwargs["package_name"]: continue f.path = Path(namespace_package, f.path) @@ -24,7 +24,7 @@ def get_files(**kwargs): files.extend(( File( - Path(metadata_directory, 'WHEEL'), + Path(metadata_directory, "WHEEL"), f"""\ Wheel-Version: 1.0 Generator: hatchling {__version__} @@ -34,17 +34,17 @@ def get_files(**kwargs): """, ), File( - Path(metadata_directory, 'METADATA'), + Path(metadata_directory, "METADATA"), f"""\ Metadata-Version: {DEFAULT_METADATA_VERSION} -Name: {kwargs['project_name']} +Name: {kwargs["project_name"]} Version: 0.0.1 License-File: LICENSE.txt """, ), )) - record_file = File(Path(metadata_directory, 'RECORD'), '') + record_file = File(Path(metadata_directory, "RECORD"), "") update_record_file_contents(record_file, files) files.append(record_file) diff --git a/tests/helpers/templates/wheel/standard_default_python_constraint.py b/tests/helpers/templates/wheel/standard_default_python_constraint.py index 611f40ba8..945ef4ad3 100644 --- a/tests/helpers/templates/wheel/standard_default_python_constraint.py +++ b/tests/helpers/templates/wheel/standard_default_python_constraint.py @@ -8,21 +8,21 @@ def get_files(**kwargs): - metadata_directory = kwargs.get('metadata_directory', '') + metadata_directory = kwargs.get("metadata_directory", "") files = [] for f in get_template_files(**kwargs): - if str(f.path) == 'LICENSE.txt': - files.append(File(Path(metadata_directory, 'licenses', f.path), f.contents)) + if str(f.path) == "LICENSE.txt": + files.append(File(Path(metadata_directory, "licenses", f.path), f.contents)) - if f.path.parts[0] != kwargs['package_name']: + if f.path.parts[0] != kwargs["package_name"]: continue files.append(f) files.extend(( File( - Path(metadata_directory, 'WHEEL'), + Path(metadata_directory, "WHEEL"), f"""\ Wheel-Version: 1.0 Generator: hatchling {__version__} @@ -31,10 +31,10 @@ def get_files(**kwargs): """, ), File( - Path(metadata_directory, 'METADATA'), + Path(metadata_directory, "METADATA"), f"""\ Metadata-Version: {DEFAULT_METADATA_VERSION} -Name: {kwargs['project_name']} +Name: {kwargs["project_name"]} Version: 0.0.1 License-File: LICENSE.txt Requires-Python: >3 @@ -42,7 +42,7 @@ def get_files(**kwargs): ), )) - record_file = File(Path(metadata_directory, 'RECORD'), '') + record_file = File(Path(metadata_directory, "RECORD"), "") update_record_file_contents(record_file, files) files.append(record_file) diff --git a/tests/helpers/templates/wheel/standard_default_python_constraint_three_components.py b/tests/helpers/templates/wheel/standard_default_python_constraint_three_components.py index e1efbb359..26cb9fa53 100644 --- a/tests/helpers/templates/wheel/standard_default_python_constraint_three_components.py +++ b/tests/helpers/templates/wheel/standard_default_python_constraint_three_components.py @@ -8,21 +8,21 @@ def get_files(**kwargs): - metadata_directory = kwargs.get('metadata_directory', '') + metadata_directory = kwargs.get("metadata_directory", "") files = [] for f in get_template_files(**kwargs): - if str(f.path) == 'LICENSE.txt': - files.append(File(Path(metadata_directory, 'licenses', f.path), f.contents)) + if str(f.path) == "LICENSE.txt": + files.append(File(Path(metadata_directory, "licenses", f.path), f.contents)) - if f.path.parts[0] != kwargs['package_name']: + if f.path.parts[0] != kwargs["package_name"]: continue files.append(f) files.extend(( File( - Path(metadata_directory, 'WHEEL'), + Path(metadata_directory, "WHEEL"), f"""\ Wheel-Version: 1.0 Generator: hatchling {__version__} @@ -31,10 +31,10 @@ def get_files(**kwargs): """, ), File( - Path(metadata_directory, 'METADATA'), + Path(metadata_directory, "METADATA"), f"""\ Metadata-Version: {DEFAULT_METADATA_VERSION} -Name: {kwargs['project_name']} +Name: {kwargs["project_name"]} Version: 0.0.1 License-File: LICENSE.txt Requires-Python: ==3.11.4 @@ -42,7 +42,7 @@ def get_files(**kwargs): ), )) - record_file = File(Path(metadata_directory, 'RECORD'), '') + record_file = File(Path(metadata_directory, "RECORD"), "") update_record_file_contents(record_file, files) files.append(record_file) diff --git a/tests/helpers/templates/wheel/standard_default_shared_data.py b/tests/helpers/templates/wheel/standard_default_shared_data.py index 3132c32ad..f74b69247 100644 --- a/tests/helpers/templates/wheel/standard_default_shared_data.py +++ b/tests/helpers/templates/wheel/standard_default_shared_data.py @@ -8,24 +8,24 @@ def get_files(**kwargs): - metadata_directory = kwargs.get('metadata_directory', '') - shared_data_directory = kwargs.get('shared_data_directory', '') + metadata_directory = kwargs.get("metadata_directory", "") + shared_data_directory = kwargs.get("shared_data_directory", "") files = [] for f in get_template_files(**kwargs): - if str(f.path) == 'LICENSE.txt': - files.append(File(Path(metadata_directory, 'licenses', f.path), f.contents)) + if str(f.path) == "LICENSE.txt": + files.append(File(Path(metadata_directory, "licenses", f.path), f.contents)) - if f.path.parts[0] != kwargs['package_name']: + if f.path.parts[0] != kwargs["package_name"]: continue files.append(f) files.extend(( - File(Path(shared_data_directory, 'data', 'foo.txt'), ''), - File(Path(shared_data_directory, 'data', 'nested', 'bar.txt'), ''), + File(Path(shared_data_directory, "data", "foo.txt"), ""), + File(Path(shared_data_directory, "data", "nested", "bar.txt"), ""), File( - Path(metadata_directory, 'WHEEL'), + Path(metadata_directory, "WHEEL"), f"""\ Wheel-Version: 1.0 Generator: hatchling {__version__} @@ -34,10 +34,10 @@ def get_files(**kwargs): """, ), File( - Path(metadata_directory, 'METADATA'), + Path(metadata_directory, "METADATA"), f"""\ Metadata-Version: {DEFAULT_METADATA_VERSION} -Name: {kwargs['project_name']} +Name: {kwargs["project_name"]} Version: 0.0.1 License-File: LICENSE.txt Requires-Python: >3 @@ -45,7 +45,7 @@ def get_files(**kwargs): ), )) - record_file = File(Path(metadata_directory, 'RECORD'), '') + record_file = File(Path(metadata_directory, "RECORD"), "") update_record_file_contents(record_file, files) files.append(record_file) diff --git a/tests/helpers/templates/wheel/standard_default_shared_scripts.py b/tests/helpers/templates/wheel/standard_default_shared_scripts.py index b89c997bd..1e52e6862 100644 --- a/tests/helpers/templates/wheel/standard_default_shared_scripts.py +++ b/tests/helpers/templates/wheel/standard_default_shared_scripts.py @@ -8,59 +8,59 @@ def get_files(**kwargs): - metadata_directory = kwargs.get('metadata_directory', '') - shared_data_directory = kwargs.get('shared_data_directory', '') - binary_contents = kwargs.get('binary_contents', b'') + metadata_directory = kwargs.get("metadata_directory", "") + shared_data_directory = kwargs.get("shared_data_directory", "") + binary_contents = kwargs.get("binary_contents", b"") files = [] for f in get_template_files(**kwargs): - if str(f.path) == 'LICENSE.txt': - files.append(File(Path(metadata_directory, 'licenses', f.path), f.contents)) + if str(f.path) == "LICENSE.txt": + files.append(File(Path(metadata_directory, "licenses", f.path), f.contents)) - if f.path.parts[0] != kwargs['package_name']: + if f.path.parts[0] != kwargs["package_name"]: continue files.append(f) files.extend(( - File(Path(shared_data_directory, 'scripts', 'binary'), binary_contents), + File(Path(shared_data_directory, "scripts", "binary"), binary_contents), File( - Path(shared_data_directory, 'scripts', 'other_script.sh'), + Path(shared_data_directory, "scripts", "other_script.sh"), """\ #!/bin/sh arg1 arg2 echo "Hello, World!" """, ), File( - Path(shared_data_directory, 'scripts', 'python_script.sh'), + Path(shared_data_directory, "scripts", "python_script.sh"), """\ #!python arg1 arg2 print("Hello, World!") """, ), File( - Path(shared_data_directory, 'scripts', 'pythonw_script.sh'), + Path(shared_data_directory, "scripts", "pythonw_script.sh"), """\ #!python arg1 arg2 print("Hello, World!") """, ), File( - Path(shared_data_directory, 'scripts', 'pypy_script.sh'), + Path(shared_data_directory, "scripts", "pypy_script.sh"), """\ #!python print("Hello, World!") """, ), File( - Path(shared_data_directory, 'scripts', 'pypyw_script.sh'), + Path(shared_data_directory, "scripts", "pypyw_script.sh"), """\ #!python arg1 arg2 print("Hello, World!") """, ), File( - Path(metadata_directory, 'WHEEL'), + Path(metadata_directory, "WHEEL"), f"""\ Wheel-Version: 1.0 Generator: hatchling {__version__} @@ -69,10 +69,10 @@ def get_files(**kwargs): """, ), File( - Path(metadata_directory, 'METADATA'), + Path(metadata_directory, "METADATA"), f"""\ Metadata-Version: {DEFAULT_METADATA_VERSION} -Name: {kwargs['project_name']} +Name: {kwargs["project_name"]} Version: 0.0.1 License-File: LICENSE.txt Requires-Python: >3 @@ -80,7 +80,7 @@ def get_files(**kwargs): ), )) - record_file = File(Path(metadata_directory, 'RECORD'), '') + record_file = File(Path(metadata_directory, "RECORD"), "") update_record_file_contents(record_file, files) files.append(record_file) diff --git a/tests/helpers/templates/wheel/standard_default_single_module.py b/tests/helpers/templates/wheel/standard_default_single_module.py index 00da83449..7d01d8ae2 100644 --- a/tests/helpers/templates/wheel/standard_default_single_module.py +++ b/tests/helpers/templates/wheel/standard_default_single_module.py @@ -8,18 +8,18 @@ def get_files(**kwargs): - metadata_directory = kwargs.get('metadata_directory', '') + metadata_directory = kwargs.get("metadata_directory", "") files = [ - File(Path(metadata_directory, 'licenses', f.path), f.contents) + File(Path(metadata_directory, "licenses", f.path), f.contents) for f in get_template_files(**kwargs) - if str(f.path) == 'LICENSE.txt' + if str(f.path) == "LICENSE.txt" ] files.extend(( - File(Path('my_app.py'), ''), + File(Path("my_app.py"), ""), File( - Path(metadata_directory, 'WHEEL'), + Path(metadata_directory, "WHEEL"), f"""\ Wheel-Version: 1.0 Generator: hatchling {__version__} @@ -29,17 +29,17 @@ def get_files(**kwargs): """, ), File( - Path(metadata_directory, 'METADATA'), + Path(metadata_directory, "METADATA"), f"""\ Metadata-Version: {DEFAULT_METADATA_VERSION} -Name: {kwargs['project_name']} +Name: {kwargs["project_name"]} Version: 0.0.1 License-File: LICENSE.txt """, ), )) - record_file = File(Path(metadata_directory, 'RECORD'), '') + record_file = File(Path(metadata_directory, "RECORD"), "") update_record_file_contents(record_file, files) files.append(record_file) diff --git a/tests/helpers/templates/wheel/standard_default_symlink.py b/tests/helpers/templates/wheel/standard_default_symlink.py index c328eafe8..1e5374bb3 100644 --- a/tests/helpers/templates/wheel/standard_default_symlink.py +++ b/tests/helpers/templates/wheel/standard_default_symlink.py @@ -8,34 +8,34 @@ def get_files(**kwargs): - metadata_directory = kwargs.get('metadata_directory', '') + metadata_directory = kwargs.get("metadata_directory", "") files = [] for f in get_template_files(**kwargs): - if str(f.path) == 'LICENSE.txt': - files.append(File(Path(metadata_directory, 'licenses', f.path), f.contents)) + if str(f.path) == "LICENSE.txt": + files.append(File(Path(metadata_directory, "licenses", f.path), f.contents)) - if f.path.parts[0] != kwargs['package_name']: + if f.path.parts[0] != kwargs["package_name"]: continue files.append(f) files.extend(( - File(Path(kwargs['package_name'], 'lib.so'), 'data'), + File(Path(kwargs["package_name"], "lib.so"), "data"), File( - Path(metadata_directory, 'WHEEL'), + Path(metadata_directory, "WHEEL"), f"""\ Wheel-Version: 1.0 Generator: hatchling {__version__} Root-Is-Purelib: false -Tag: {kwargs.get('tag', '')} +Tag: {kwargs.get("tag", "")} """, ), File( - Path(metadata_directory, 'METADATA'), + Path(metadata_directory, "METADATA"), f"""\ Metadata-Version: {DEFAULT_METADATA_VERSION} -Name: {kwargs['project_name']} +Name: {kwargs["project_name"]} Version: 0.0.1 License-File: LICENSE.txt Requires-Python: >3 @@ -43,7 +43,7 @@ def get_files(**kwargs): ), )) - record_file = File(Path(metadata_directory, 'RECORD'), '') + record_file = File(Path(metadata_directory, "RECORD"), "") update_record_file_contents(record_file, files) files.append(record_file) diff --git a/tests/helpers/templates/wheel/standard_editable_exact.py b/tests/helpers/templates/wheel/standard_editable_exact.py index 25d574091..491be227d 100644 --- a/tests/helpers/templates/wheel/standard_editable_exact.py +++ b/tests/helpers/templates/wheel/standard_editable_exact.py @@ -8,13 +8,13 @@ def get_files(**kwargs): - metadata_directory = kwargs.get('metadata_directory', '') - package_root = kwargs.get('package_root', '') + metadata_directory = kwargs.get("metadata_directory", "") + package_root = kwargs.get("package_root", "") files = [ - File(Path(metadata_directory, 'licenses', f.path), f.contents) + File(Path(metadata_directory, "licenses", f.path), f.contents) for f in get_template_files(**kwargs) - if str(f.path) == 'LICENSE.txt' + if str(f.path) == "LICENSE.txt" ] pth_file_name = f"_{kwargs['package_name']}.pth" @@ -26,10 +26,10 @@ def get_files(**kwargs): f"""\ from editables.redirector import RedirectingFinder as F F.install() -F.map_module({kwargs['package_name']!r}, {package_root!r})""", +F.map_module({kwargs["package_name"]!r}, {package_root!r})""", ), File( - Path(metadata_directory, 'WHEEL'), + Path(metadata_directory, "WHEEL"), f"""\ Wheel-Version: 1.0 Generator: hatchling {__version__} @@ -39,10 +39,10 @@ def get_files(**kwargs): """, ), File( - Path(metadata_directory, 'METADATA'), + Path(metadata_directory, "METADATA"), f"""\ Metadata-Version: {DEFAULT_METADATA_VERSION} -Name: {kwargs['project_name']} +Name: {kwargs["project_name"]} Version: 0.0.1 License-File: LICENSE.txt Requires-Dist: editables~=0.3 @@ -50,7 +50,7 @@ def get_files(**kwargs): ), )) - record_file = File(Path(metadata_directory, 'RECORD'), '') + record_file = File(Path(metadata_directory, "RECORD"), "") update_record_file_contents(record_file, files, generated_files={pth_file_name, loader_file_name}) files.append(record_file) diff --git a/tests/helpers/templates/wheel/standard_editable_exact_extra_dependencies.py b/tests/helpers/templates/wheel/standard_editable_exact_extra_dependencies.py index 5d25e809a..817a643eb 100644 --- a/tests/helpers/templates/wheel/standard_editable_exact_extra_dependencies.py +++ b/tests/helpers/templates/wheel/standard_editable_exact_extra_dependencies.py @@ -8,13 +8,13 @@ def get_files(**kwargs): - metadata_directory = kwargs.get('metadata_directory', '') - package_root = kwargs.get('package_root', '') + metadata_directory = kwargs.get("metadata_directory", "") + package_root = kwargs.get("package_root", "") files = [ - File(Path(metadata_directory, 'licenses', f.path), f.contents) + File(Path(metadata_directory, "licenses", f.path), f.contents) for f in get_template_files(**kwargs) - if str(f.path) == 'LICENSE.txt' + if str(f.path) == "LICENSE.txt" ] pth_file_name = f"_{kwargs['package_name']}.pth" @@ -26,10 +26,10 @@ def get_files(**kwargs): f"""\ from editables.redirector import RedirectingFinder as F F.install() -F.map_module({kwargs['package_name']!r}, {package_root!r})""", +F.map_module({kwargs["package_name"]!r}, {package_root!r})""", ), File( - Path(metadata_directory, 'WHEEL'), + Path(metadata_directory, "WHEEL"), f"""\ Wheel-Version: 1.0 Generator: hatchling {__version__} @@ -39,10 +39,10 @@ def get_files(**kwargs): """, ), File( - Path(metadata_directory, 'METADATA'), + Path(metadata_directory, "METADATA"), f"""\ Metadata-Version: {DEFAULT_METADATA_VERSION} -Name: {kwargs['project_name']} +Name: {kwargs["project_name"]} Version: 0.0.1 License-File: LICENSE.txt Requires-Dist: binary @@ -51,7 +51,7 @@ def get_files(**kwargs): ), )) - record_file = File(Path(metadata_directory, 'RECORD'), '') + record_file = File(Path(metadata_directory, "RECORD"), "") update_record_file_contents(record_file, files, generated_files={pth_file_name, loader_file_name}) files.append(record_file) diff --git a/tests/helpers/templates/wheel/standard_editable_exact_force_include.py b/tests/helpers/templates/wheel/standard_editable_exact_force_include.py index 50e9dc684..e9de6159c 100644 --- a/tests/helpers/templates/wheel/standard_editable_exact_force_include.py +++ b/tests/helpers/templates/wheel/standard_editable_exact_force_include.py @@ -8,15 +8,15 @@ def get_files(**kwargs): - metadata_directory = kwargs.get('metadata_directory', '') - package_root = kwargs.get('package_root', '') + metadata_directory = kwargs.get("metadata_directory", "") + package_root = kwargs.get("package_root", "") files = [] for f in get_template_files(**kwargs): - if str(f.path) == 'LICENSE.txt': - files.append(File(Path(metadata_directory, 'licenses', f.path), f.contents)) - elif f.path.parts[-1] == '__about__.py': - files.append(File(Path('zfoo.py'), f.contents)) + if str(f.path) == "LICENSE.txt": + files.append(File(Path(metadata_directory, "licenses", f.path), f.contents)) + elif f.path.parts[-1] == "__about__.py": + files.append(File(Path("zfoo.py"), f.contents)) pth_file_name = f"_{kwargs['package_name']}.pth" loader_file_name = f"_editable_impl_{kwargs['package_name']}.py" @@ -27,10 +27,10 @@ def get_files(**kwargs): f"""\ from editables.redirector import RedirectingFinder as F F.install() -F.map_module({kwargs['package_name']!r}, {package_root!r})""", +F.map_module({kwargs["package_name"]!r}, {package_root!r})""", ), File( - Path(metadata_directory, 'WHEEL'), + Path(metadata_directory, "WHEEL"), f"""\ Wheel-Version: 1.0 Generator: hatchling {__version__} @@ -40,10 +40,10 @@ def get_files(**kwargs): """, ), File( - Path(metadata_directory, 'METADATA'), + Path(metadata_directory, "METADATA"), f"""\ Metadata-Version: {DEFAULT_METADATA_VERSION} -Name: {kwargs['project_name']} +Name: {kwargs["project_name"]} Version: 0.0.1 License-File: LICENSE.txt Requires-Dist: editables~=0.3 @@ -51,7 +51,7 @@ def get_files(**kwargs): ), )) - record_file = File(Path(metadata_directory, 'RECORD'), '') + record_file = File(Path(metadata_directory, "RECORD"), "") update_record_file_contents(record_file, files, generated_files={pth_file_name, loader_file_name}) files.append(record_file) diff --git a/tests/helpers/templates/wheel/standard_editable_pth.py b/tests/helpers/templates/wheel/standard_editable_pth.py index bb6eb04ce..5a170cc6a 100644 --- a/tests/helpers/templates/wheel/standard_editable_pth.py +++ b/tests/helpers/templates/wheel/standard_editable_pth.py @@ -8,20 +8,20 @@ def get_files(**kwargs): - metadata_directory = kwargs.get('metadata_directory', '') - package_paths = kwargs.get('package_paths', []) + metadata_directory = kwargs.get("metadata_directory", "") + package_paths = kwargs.get("package_paths", []) files = [ - File(Path(metadata_directory, 'licenses', f.path), f.contents) + File(Path(metadata_directory, "licenses", f.path), f.contents) for f in get_template_files(**kwargs) - if str(f.path) == 'LICENSE.txt' + if str(f.path) == "LICENSE.txt" ] pth_file_name = f"_{kwargs['package_name']}.pth" files.extend(( - File(Path(pth_file_name), '\n'.join(package_paths)), + File(Path(pth_file_name), "\n".join(package_paths)), File( - Path(metadata_directory, 'WHEEL'), + Path(metadata_directory, "WHEEL"), f"""\ Wheel-Version: 1.0 Generator: hatchling {__version__} @@ -31,17 +31,17 @@ def get_files(**kwargs): """, ), File( - Path(metadata_directory, 'METADATA'), + Path(metadata_directory, "METADATA"), f"""\ Metadata-Version: {DEFAULT_METADATA_VERSION} -Name: {kwargs['project_name']} +Name: {kwargs["project_name"]} Version: 0.0.1 License-File: LICENSE.txt """, ), )) - record_file = File(Path(metadata_directory, 'RECORD'), '') + record_file = File(Path(metadata_directory, "RECORD"), "") update_record_file_contents(record_file, files, generated_files={pth_file_name}) files.append(record_file) diff --git a/tests/helpers/templates/wheel/standard_editable_pth_extra_dependencies.py b/tests/helpers/templates/wheel/standard_editable_pth_extra_dependencies.py index 8319e36a8..834383259 100644 --- a/tests/helpers/templates/wheel/standard_editable_pth_extra_dependencies.py +++ b/tests/helpers/templates/wheel/standard_editable_pth_extra_dependencies.py @@ -8,20 +8,20 @@ def get_files(**kwargs): - metadata_directory = kwargs.get('metadata_directory', '') - package_paths = kwargs.get('package_paths', []) + metadata_directory = kwargs.get("metadata_directory", "") + package_paths = kwargs.get("package_paths", []) files = [ - File(Path(metadata_directory, 'licenses', f.path), f.contents) + File(Path(metadata_directory, "licenses", f.path), f.contents) for f in get_template_files(**kwargs) - if str(f.path) == 'LICENSE.txt' + if str(f.path) == "LICENSE.txt" ] pth_file_name = f"_{kwargs['package_name']}.pth" files.extend(( - File(Path(pth_file_name), '\n'.join(package_paths)), + File(Path(pth_file_name), "\n".join(package_paths)), File( - Path(metadata_directory, 'WHEEL'), + Path(metadata_directory, "WHEEL"), f"""\ Wheel-Version: 1.0 Generator: hatchling {__version__} @@ -31,10 +31,10 @@ def get_files(**kwargs): """, ), File( - Path(metadata_directory, 'METADATA'), + Path(metadata_directory, "METADATA"), f"""\ Metadata-Version: {DEFAULT_METADATA_VERSION} -Name: {kwargs['project_name']} +Name: {kwargs["project_name"]} Version: 0.0.1 License-File: LICENSE.txt Requires-Dist: binary @@ -42,7 +42,7 @@ def get_files(**kwargs): ), )) - record_file = File(Path(metadata_directory, 'RECORD'), '') + record_file = File(Path(metadata_directory, "RECORD"), "") update_record_file_contents(record_file, files, generated_files={pth_file_name}) files.append(record_file) diff --git a/tests/helpers/templates/wheel/standard_editable_pth_force_include.py b/tests/helpers/templates/wheel/standard_editable_pth_force_include.py index 9c46c46e1..003954027 100644 --- a/tests/helpers/templates/wheel/standard_editable_pth_force_include.py +++ b/tests/helpers/templates/wheel/standard_editable_pth_force_include.py @@ -8,21 +8,21 @@ def get_files(**kwargs): - metadata_directory = kwargs.get('metadata_directory', '') - package_paths = kwargs.get('package_paths', []) + metadata_directory = kwargs.get("metadata_directory", "") + package_paths = kwargs.get("package_paths", []) files = [] for f in get_template_files(**kwargs): - if str(f.path) == 'LICENSE.txt': - files.append(File(Path(metadata_directory, 'licenses', f.path), f.contents)) - elif f.path.parts[-1] == '__about__.py': - files.append(File(Path('zfoo.py'), f.contents)) + if str(f.path) == "LICENSE.txt": + files.append(File(Path(metadata_directory, "licenses", f.path), f.contents)) + elif f.path.parts[-1] == "__about__.py": + files.append(File(Path("zfoo.py"), f.contents)) pth_file_name = f"_{kwargs['package_name']}.pth" files.extend(( - File(Path(pth_file_name), '\n'.join(package_paths)), + File(Path(pth_file_name), "\n".join(package_paths)), File( - Path(metadata_directory, 'WHEEL'), + Path(metadata_directory, "WHEEL"), f"""\ Wheel-Version: 1.0 Generator: hatchling {__version__} @@ -32,17 +32,17 @@ def get_files(**kwargs): """, ), File( - Path(metadata_directory, 'METADATA'), + Path(metadata_directory, "METADATA"), f"""\ Metadata-Version: {DEFAULT_METADATA_VERSION} -Name: {kwargs['project_name']} +Name: {kwargs["project_name"]} Version: 0.0.1 License-File: LICENSE.txt """, ), )) - record_file = File(Path(metadata_directory, 'RECORD'), '') + record_file = File(Path(metadata_directory, "RECORD"), "") update_record_file_contents(record_file, files, generated_files={pth_file_name}) files.append(record_file) diff --git a/tests/helpers/templates/wheel/standard_entry_points.py b/tests/helpers/templates/wheel/standard_entry_points.py index 4c97d9e8d..c4b080c1b 100644 --- a/tests/helpers/templates/wheel/standard_entry_points.py +++ b/tests/helpers/templates/wheel/standard_entry_points.py @@ -8,21 +8,21 @@ def get_files(**kwargs): - metadata_directory = kwargs.get('metadata_directory', '') + metadata_directory = kwargs.get("metadata_directory", "") files = [] for f in get_template_files(**kwargs): - if str(f.path) == 'LICENSE.txt': - files.append(File(Path(metadata_directory, 'licenses', f.path), f.contents)) + if str(f.path) == "LICENSE.txt": + files.append(File(Path(metadata_directory, "licenses", f.path), f.contents)) - if f.path.parts[0] != kwargs['package_name']: + if f.path.parts[0] != kwargs["package_name"]: continue files.append(f) files.extend(( File( - Path(metadata_directory, 'entry_points.txt'), + Path(metadata_directory, "entry_points.txt"), """\ [console_scripts] bar = pkg:foo @@ -30,7 +30,7 @@ def get_files(**kwargs): """, ), File( - Path(metadata_directory, 'WHEEL'), + Path(metadata_directory, "WHEEL"), f"""\ Wheel-Version: 1.0 Generator: hatchling {__version__} @@ -40,17 +40,17 @@ def get_files(**kwargs): """, ), File( - Path(metadata_directory, 'METADATA'), + Path(metadata_directory, "METADATA"), f"""\ Metadata-Version: {DEFAULT_METADATA_VERSION} -Name: {kwargs['project_name']} +Name: {kwargs["project_name"]} Version: 0.0.1 License-File: LICENSE.txt """, ), )) - record_file = File(Path(metadata_directory, 'RECORD'), '') + record_file = File(Path(metadata_directory, "RECORD"), "") update_record_file_contents(record_file, files) files.append(record_file) diff --git a/tests/helpers/templates/wheel/standard_no_strict_naming.py b/tests/helpers/templates/wheel/standard_no_strict_naming.py index d02eefed8..eada5e1a8 100644 --- a/tests/helpers/templates/wheel/standard_no_strict_naming.py +++ b/tests/helpers/templates/wheel/standard_no_strict_naming.py @@ -8,21 +8,21 @@ def get_files(**kwargs): - metadata_directory = kwargs.get('metadata_directory', '') + metadata_directory = kwargs.get("metadata_directory", "") files = [] for f in get_template_files(**kwargs): - if str(f.path) == 'LICENSE.txt': - files.append(File(Path(metadata_directory, 'licenses', f.path), f.contents)) + if str(f.path) == "LICENSE.txt": + files.append(File(Path(metadata_directory, "licenses", f.path), f.contents)) - if f.path.parts[0] != kwargs['package_name']: + if f.path.parts[0] != kwargs["package_name"]: continue files.append(f) files.extend(( File( - Path(metadata_directory, 'WHEEL'), + Path(metadata_directory, "WHEEL"), f"""\ Wheel-Version: 1.0 Generator: hatchling {__version__} @@ -32,17 +32,17 @@ def get_files(**kwargs): """, ), File( - Path(metadata_directory, 'METADATA'), + Path(metadata_directory, "METADATA"), f"""\ Metadata-Version: {DEFAULT_METADATA_VERSION} -Name: {kwargs['project_name']} +Name: {kwargs["project_name"]} Version: 0.0.1 License-File: LICENSE.txt """, ), )) - record_file = File(Path(metadata_directory, 'RECORD'), '') + record_file = File(Path(metadata_directory, "RECORD"), "") update_record_file_contents(record_file, files) files.append(record_file) diff --git a/tests/helpers/templates/wheel/standard_only_packages_artifact_override.py b/tests/helpers/templates/wheel/standard_only_packages_artifact_override.py index 25c1721c5..5c4845f67 100644 --- a/tests/helpers/templates/wheel/standard_only_packages_artifact_override.py +++ b/tests/helpers/templates/wheel/standard_only_packages_artifact_override.py @@ -8,24 +8,24 @@ def get_files(**kwargs): - metadata_directory = kwargs.get('metadata_directory', '') + metadata_directory = kwargs.get("metadata_directory", "") files = [] for f in get_template_files(**kwargs): - if str(f.path) == 'LICENSE.txt': - files.append(File(Path(metadata_directory, 'licenses', f.path), f.contents)) + if str(f.path) == "LICENSE.txt": + files.append(File(Path(metadata_directory, "licenses", f.path), f.contents)) - if f.path.parts[0] not in {kwargs['package_name'], 'tests'}: + if f.path.parts[0] not in {kwargs["package_name"], "tests"}: continue - if f.path == Path('tests', '__init__.py'): - f.path = Path('tests', 'foo.py') + if f.path == Path("tests", "__init__.py"): + f.path = Path("tests", "foo.py") files.append(f) files.extend(( File( - Path(metadata_directory, 'WHEEL'), + Path(metadata_directory, "WHEEL"), f"""\ Wheel-Version: 1.0 Generator: hatchling {__version__} @@ -35,17 +35,17 @@ def get_files(**kwargs): """, ), File( - Path(metadata_directory, 'METADATA'), + Path(metadata_directory, "METADATA"), f"""\ Metadata-Version: {DEFAULT_METADATA_VERSION} -Name: {kwargs['project_name']} +Name: {kwargs["project_name"]} Version: 0.0.1 License-File: LICENSE.txt """, ), )) - record_file = File(Path(metadata_directory, 'RECORD'), '') + record_file = File(Path(metadata_directory, "RECORD"), "") update_record_file_contents(record_file, files) files.append(record_file) diff --git a/tests/helpers/templates/wheel/standard_tests.py b/tests/helpers/templates/wheel/standard_tests.py index 7ff087fd9..07a0eb5d7 100644 --- a/tests/helpers/templates/wheel/standard_tests.py +++ b/tests/helpers/templates/wheel/standard_tests.py @@ -8,21 +8,21 @@ def get_files(**kwargs): - metadata_directory = kwargs.get('metadata_directory', '') + metadata_directory = kwargs.get("metadata_directory", "") files = [] for f in get_template_files(**kwargs): - if str(f.path) == 'LICENSE.txt': - files.append(File(Path(metadata_directory, 'licenses', f.path), f.contents)) + if str(f.path) == "LICENSE.txt": + files.append(File(Path(metadata_directory, "licenses", f.path), f.contents)) - if f.path.parts[0] not in {kwargs['package_name'], 'tests'}: + if f.path.parts[0] not in {kwargs["package_name"], "tests"}: continue files.append(f) files.extend(( File( - Path(metadata_directory, 'WHEEL'), + Path(metadata_directory, "WHEEL"), f"""\ Wheel-Version: 1.0 Generator: hatchling {__version__} @@ -32,17 +32,17 @@ def get_files(**kwargs): """, ), File( - Path(metadata_directory, 'METADATA'), + Path(metadata_directory, "METADATA"), f"""\ Metadata-Version: {DEFAULT_METADATA_VERSION} -Name: {kwargs['project_name']} +Name: {kwargs["project_name"]} Version: 0.0.1 License-File: LICENSE.txt """, ), )) - record_file = File(Path(metadata_directory, 'RECORD'), '') + record_file = File(Path(metadata_directory, "RECORD"), "") update_record_file_contents(record_file, files) files.append(record_file) diff --git a/tests/helpers/templates/wheel/utils.py b/tests/helpers/templates/wheel/utils.py index 35e355bc5..0bca8707f 100644 --- a/tests/helpers/templates/wheel/utils.py +++ b/tests/helpers/templates/wheel/utils.py @@ -9,9 +9,9 @@ def update_record_file_contents(record_file, files, generated_files=()): for template_file in sorted( files, key=lambda f: ( - f.path.parts[0].endswith('.dist-info'), - f.path.parts[0].endswith('.dist-info') and f.path.parts[1] == 'extra_metadata', - f.path.parts[0].startswith('z'), + f.path.parts[0].endswith(".dist-info"), + f.path.parts[0].endswith(".dist-info") and f.path.parts[1] == "extra_metadata", + f.path.parts[0].startswith("z"), len(f.path.parts), f.path.parts, ), @@ -21,28 +21,28 @@ def update_record_file_contents(record_file, files, generated_files=()): raw_contents = template_file.contents else: is_binary = False - raw_contents = template_file.contents.encode('utf-8') + raw_contents = template_file.contents.encode("utf-8") template_file_path = str(template_file.path) if ( not is_binary - and os.linesep != '\n' + and os.linesep != "\n" and ( - 'LICENSE' in template_file_path + "LICENSE" in template_file_path or ( - not template_file.path.parts[0].endswith('.dist-info') + not template_file.path.parts[0].endswith(".dist-info") and all(f not in template_file_path for f in generated_files) ) ) ): - raw_contents = raw_contents.replace(b'\n', b'\r\n') + raw_contents = raw_contents.replace(b"\n", b"\r\n") hash_obj = hashlib.sha256() hash_obj.update(raw_contents) hash_digest = format_file_hash(hash_obj.digest()) - record_file.contents += f'{template_file.path.as_posix()},sha256={hash_digest},{len(raw_contents)}\n' + record_file.contents += f"{template_file.path.as_posix()},sha256={hash_digest},{len(raw_contents)}\n" - record_file.contents += f'{record_file.path.as_posix()},,\n' + record_file.contents += f"{record_file.path.as_posix()},,\n" def test_normalize_artifact_permissions(): diff --git a/tests/index/test_core.py b/tests/index/test_core.py index 9f6b32c1a..2095629a2 100644 --- a/tests/index/test_core.py +++ b/tests/index/test_core.py @@ -5,18 +5,18 @@ class TestRepo: def test_normalization(self): - index = PackageIndex('Https://Foo.Internal/z/../a/b/') + index = PackageIndex("Https://Foo.Internal/z/../a/b/") - assert index.repo == 'https://foo.internal/a/b/' + assert index.repo == "https://foo.internal/a/b/" class TestURLs: @pytest.mark.parametrize( - ('repo_url', 'expected_url'), + ("repo_url", "expected_url"), [ - pytest.param('https://upload.pypi.org/legacy/', 'https://pypi.org/simple/', id='PyPI main'), - pytest.param('https://test.pypi.org/legacy/', 'https://test.pypi.org/simple/', id='PyPI test'), - pytest.param('https://foo.internal/a/b/', 'https://foo.internal/a/b/%2Bsimple/', id='default'), + pytest.param("https://upload.pypi.org/legacy/", "https://pypi.org/simple/", id="PyPI main"), + pytest.param("https://test.pypi.org/legacy/", "https://test.pypi.org/simple/", id="PyPI test"), + pytest.param("https://foo.internal/a/b/", "https://foo.internal/a/b/%2Bsimple/", id="default"), ], ) def test_simple(self, repo_url, expected_url): @@ -25,11 +25,11 @@ def test_simple(self, repo_url, expected_url): assert str(index.urls.simple) == expected_url @pytest.mark.parametrize( - ('repo_url', 'expected_url'), + ("repo_url", "expected_url"), [ - pytest.param('https://upload.pypi.org/legacy/', 'https://pypi.org/project/', id='PyPI main'), - pytest.param('https://test.pypi.org/legacy/', 'https://test.pypi.org/project/', id='PyPI test'), - pytest.param('https://foo.internal/a/b/', 'https://foo.internal/a/b/', id='default'), + pytest.param("https://upload.pypi.org/legacy/", "https://pypi.org/project/", id="PyPI main"), + pytest.param("https://test.pypi.org/legacy/", "https://test.pypi.org/project/", id="PyPI test"), + pytest.param("https://foo.internal/a/b/", "https://foo.internal/a/b/", id="default"), ], ) def test_project(self, repo_url, expected_url): @@ -40,29 +40,29 @@ def test_project(self, repo_url, expected_url): class TestTLS: def test_default(self, mocker): - mock = mocker.patch('httpx._transports.default.create_ssl_context') - index = PackageIndex('https://foo.internal/a/b/') + mock = mocker.patch("httpx._transports.default.create_ssl_context") + index = PackageIndex("https://foo.internal/a/b/") _ = index.client mock.assert_called_once_with(verify=True, cert=None, trust_env=True) def test_ca_cert(self, mocker): - mock = mocker.patch('httpx._transports.default.create_ssl_context') - index = PackageIndex('https://foo.internal/a/b/', ca_cert='foo') + mock = mocker.patch("httpx._transports.default.create_ssl_context") + index = PackageIndex("https://foo.internal/a/b/", ca_cert="foo") _ = index.client - mock.assert_called_once_with(verify='foo', cert=None, trust_env=True) + mock.assert_called_once_with(verify="foo", cert=None, trust_env=True) def test_client_cert(self, mocker): - mock = mocker.patch('httpx._transports.default.create_ssl_context') - index = PackageIndex('https://foo.internal/a/b/', client_cert='foo') + mock = mocker.patch("httpx._transports.default.create_ssl_context") + index = PackageIndex("https://foo.internal/a/b/", client_cert="foo") _ = index.client - mock.assert_called_once_with(verify=True, cert='foo', trust_env=True) + mock.assert_called_once_with(verify=True, cert="foo", trust_env=True) def test_client_cert_with_key(self, mocker): - mock = mocker.patch('httpx._transports.default.create_ssl_context') - index = PackageIndex('https://foo.internal/a/b/', client_cert='foo', client_key='bar') + mock = mocker.patch("httpx._transports.default.create_ssl_context") + index = PackageIndex("https://foo.internal/a/b/", client_cert="foo", client_key="bar") _ = index.client - mock.assert_called_once_with(verify=True, cert=('foo', 'bar'), trust_env=True) + mock.assert_called_once_with(verify=True, cert=("foo", "bar"), trust_env=True) diff --git a/tests/project/test_config.py b/tests/project/test_config.py index 1edd48d49..f7aef946e 100644 --- a/tests/project/test_config.py +++ b/tests/project/test_config.py @@ -12,14 +12,14 @@ ARRAY_OPTIONS = [o for o, t in RESERVED_OPTIONS.items() if t is list] BOOLEAN_OPTIONS = [o for o, t in RESERVED_OPTIONS.items() if t is bool] MAPPING_OPTIONS = [o for o, t in RESERVED_OPTIONS.items() if t is dict] -STRING_OPTIONS = [o for o, t in RESERVED_OPTIONS.items() if t is str and o != 'matrix-name-format'] +STRING_OPTIONS = [o for o, t in RESERVED_OPTIONS.items() if t is str and o != "matrix-name-format"] def construct_matrix_data(env_name, config, overrides=None): config = dict(config[env_name]) - config.pop('overrides', None) - matrices = config.pop('matrix') - final_matrix_name_format = config.pop('matrix-name-format', '{value}') + config.pop("overrides", None) + matrices = config.pop("matrix") + final_matrix_name_format = config.pop("matrix-name-format", "{value}") # [{'version': ['9000']}, {'feature': ['bar']}] envs = {} @@ -27,7 +27,7 @@ def construct_matrix_data(env_name, config, overrides=None): matrix = dict(matrix_data) variables = {} python_selected = False - for variable in ('py', 'python'): + for variable in ("py", "python"): if variable in matrix: python_selected = True variables[variable] = matrix.pop(variable) @@ -39,27 +39,27 @@ def construct_matrix_data(env_name, config, overrides=None): env_name_parts = [] for j, (variable, value) in enumerate(variable_values.items()): if j == 0 and python_selected: - env_name_parts.append(value if value.startswith('py') else f'py{value}') + env_name_parts.append(value if value.startswith("py") else f"py{value}") else: env_name_parts.append(final_matrix_name_format.format(variable=variable, value=value)) - new_env_name = '-'.join(env_name_parts) - if env_name != 'default': - new_env_name = f'{env_name}.{new_env_name}' + new_env_name = "-".join(env_name_parts) + if env_name != "default": + new_env_name = f"{env_name}.{new_env_name}" envs[new_env_name] = variable_values - if 'py' in variable_values: - envs[new_env_name] = {'python': variable_values.pop('py'), **variable_values} + if "py" in variable_values: + envs[new_env_name] = {"python": variable_values.pop("py"), **variable_values} config.update(overrides or {}) - config.setdefault('type', 'virtual') - return {'config': config, 'envs': envs} + config.setdefault("type", "virtual") + return {"config": config, "envs": envs} class TestEnv: def test_not_table(self, isolation): - with pytest.raises(TypeError, match='Field `tool.hatch.env` must be a table'): - _ = ProjectConfig(isolation, {'env': 9000}).env + with pytest.raises(TypeError, match="Field `tool.hatch.env` must be a table"): + _ = ProjectConfig(isolation, {"env": 9000}).env def test_default(self, isolation): project_config = ProjectConfig(isolation, {}) @@ -69,16 +69,16 @@ def test_default(self, isolation): class TestEnvRequires: def test_not_array(self, isolation): - with pytest.raises(TypeError, match='Field `tool.hatch.env.requires` must be an array'): - _ = ProjectConfig(isolation, {'env': {'requires': 9000}}).env_requires + with pytest.raises(TypeError, match="Field `tool.hatch.env.requires` must be an array"): + _ = ProjectConfig(isolation, {"env": {"requires": 9000}}).env_requires def test_requirement_not_string(self, isolation): - with pytest.raises(TypeError, match='Requirement #1 in `tool.hatch.env.requires` must be a string'): - _ = ProjectConfig(isolation, {'env': {'requires': [9000]}}).env_requires + with pytest.raises(TypeError, match="Requirement #1 in `tool.hatch.env.requires` must be a string"): + _ = ProjectConfig(isolation, {"env": {"requires": [9000]}}).env_requires def test_requirement_invalid(self, isolation): - with pytest.raises(ValueError, match='Requirement #1 in `tool.hatch.env.requires` is invalid: .+'): - _ = ProjectConfig(isolation, {'env': {'requires': ['foo^1']}}).env_requires + with pytest.raises(ValueError, match="Requirement #1 in `tool.hatch.env.requires` is invalid: .+"): + _ = ProjectConfig(isolation, {"env": {"requires": ["foo^1"]}}).env_requires def test_default(self, isolation): project_config = ProjectConfig(isolation, {}) @@ -87,586 +87,586 @@ def test_default(self, isolation): assert project_config.env_requires == project_config.env_requires == [] def test_defined(self, isolation): - project_config = ProjectConfig(isolation, {'env': {'requires': ['foo', 'bar', 'baz']}}) + project_config = ProjectConfig(isolation, {"env": {"requires": ["foo", "bar", "baz"]}}) - assert project_config.env_requires == ['foo', 'bar', 'baz'] + assert project_config.env_requires == ["foo", "bar", "baz"] class TestEnvCollectors: def test_not_table(self, isolation): - with pytest.raises(TypeError, match='Field `tool.hatch.env.collectors` must be a table'): - _ = ProjectConfig(isolation, {'env': {'collectors': 9000}}).env_collectors + with pytest.raises(TypeError, match="Field `tool.hatch.env.collectors` must be a table"): + _ = ProjectConfig(isolation, {"env": {"collectors": 9000}}).env_collectors def test_collector_not_table(self, isolation): - with pytest.raises(TypeError, match='Field `tool.hatch.env.collectors.foo` must be a table'): - _ = ProjectConfig(isolation, {'env': {'collectors': {'foo': 9000}}}).env_collectors + with pytest.raises(TypeError, match="Field `tool.hatch.env.collectors.foo` must be a table"): + _ = ProjectConfig(isolation, {"env": {"collectors": {"foo": 9000}}}).env_collectors def test_default(self, isolation): project_config = ProjectConfig(isolation, {}) - assert project_config.env_collectors == project_config.env_collectors == {'default': {}} + assert project_config.env_collectors == project_config.env_collectors == {"default": {}} def test_defined(self, isolation): - project_config = ProjectConfig(isolation, {'env': {'collectors': {'foo': {'bar': {'baz': 9000}}}}}) + project_config = ProjectConfig(isolation, {"env": {"collectors": {"foo": {"bar": {"baz": 9000}}}}}) - assert project_config.env_collectors == {'default': {}, 'foo': {'bar': {'baz': 9000}}} - assert list(project_config.env_collectors) == ['default', 'foo'] + assert project_config.env_collectors == {"default": {}, "foo": {"bar": {"baz": 9000}}} + assert list(project_config.env_collectors) == ["default", "foo"] class TestEnvs: def test_not_table(self, isolation): - with pytest.raises(TypeError, match='Field `tool.hatch.envs` must be a table'): - _ = ProjectConfig(isolation, {'envs': 9000}, PluginManager()).envs + with pytest.raises(TypeError, match="Field `tool.hatch.envs` must be a table"): + _ = ProjectConfig(isolation, {"envs": 9000}, PluginManager()).envs def test_config_not_table(self, isolation): - with pytest.raises(TypeError, match='Field `tool.hatch.envs.foo` must be a table'): - _ = ProjectConfig(isolation, {'envs': {'foo': 9000}}, PluginManager()).envs + with pytest.raises(TypeError, match="Field `tool.hatch.envs.foo` must be a table"): + _ = ProjectConfig(isolation, {"envs": {"foo": 9000}}, PluginManager()).envs def test_unknown_collector(self, isolation): - with pytest.raises(ValueError, match='Unknown environment collector: foo'): - _ = ProjectConfig(isolation, {'env': {'collectors': {'foo': {}}}}, PluginManager()).envs + with pytest.raises(ValueError, match="Unknown environment collector: foo"): + _ = ProjectConfig(isolation, {"env": {"collectors": {"foo": {}}}}, PluginManager()).envs def test_unknown_template(self, isolation): with pytest.raises( - ValueError, match='Field `tool.hatch.envs.foo.template` refers to an unknown environment `bar`' + ValueError, match="Field `tool.hatch.envs.foo.template` refers to an unknown environment `bar`" ): - _ = ProjectConfig(isolation, {'envs': {'foo': {'template': 'bar'}}}, PluginManager()).envs + _ = ProjectConfig(isolation, {"envs": {"foo": {"template": "bar"}}}, PluginManager()).envs def test_default_undefined(self, isolation): project_config = ProjectConfig(isolation, {}, PluginManager()) - assert project_config.envs == project_config.envs == {'default': {'type': 'virtual'}} + assert project_config.envs == project_config.envs == {"default": {"type": "virtual"}} assert project_config.matrices == project_config.matrices == {} def test_default_partially_defined(self, isolation): - env_config = {'default': {'option': True}} - project_config = ProjectConfig(isolation, {'envs': env_config}, PluginManager()) + env_config = {"default": {"option": True}} + project_config = ProjectConfig(isolation, {"envs": env_config}, PluginManager()) - assert project_config.envs == {'default': {'option': True, 'type': 'virtual'}} + assert project_config.envs == {"default": {"option": True, "type": "virtual"}} def test_default_defined(self, isolation): - env_config = {'default': {'type': 'foo'}} - project_config = ProjectConfig(isolation, {'envs': env_config}, PluginManager()) + env_config = {"default": {"type": "foo"}} + project_config = ProjectConfig(isolation, {"envs": env_config}, PluginManager()) - assert project_config.envs == {'default': {'type': 'foo'}} + assert project_config.envs == {"default": {"type": "foo"}} def test_basic(self, isolation): - env_config = {'foo': {'option': True}} - project_config = ProjectConfig(isolation, {'envs': env_config}, PluginManager()) + env_config = {"foo": {"option": True}} + project_config = ProjectConfig(isolation, {"envs": env_config}, PluginManager()) - assert project_config.envs == {'default': {'type': 'virtual'}, 'foo': {'option': True, 'type': 'virtual'}} + assert project_config.envs == {"default": {"type": "virtual"}, "foo": {"option": True, "type": "virtual"}} def test_basic_override(self, isolation): - env_config = {'foo': {'type': 'baz'}} - project_config = ProjectConfig(isolation, {'envs': env_config}, PluginManager()) + env_config = {"foo": {"type": "baz"}} + project_config = ProjectConfig(isolation, {"envs": env_config}, PluginManager()) - assert project_config.envs == {'default': {'type': 'virtual'}, 'foo': {'type': 'baz'}} + assert project_config.envs == {"default": {"type": "virtual"}, "foo": {"type": "baz"}} def test_multiple_inheritance(self, isolation): env_config = { - 'foo': {'option1': 'foo'}, - 'bar': {'template': 'foo', 'option2': 'bar'}, - 'baz': {'template': 'bar', 'option3': 'baz'}, + "foo": {"option1": "foo"}, + "bar": {"template": "foo", "option2": "bar"}, + "baz": {"template": "bar", "option3": "baz"}, } - project_config = ProjectConfig(isolation, {'envs': env_config}, PluginManager()) + project_config = ProjectConfig(isolation, {"envs": env_config}, PluginManager()) assert project_config.envs == { - 'default': {'type': 'virtual'}, - 'foo': {'type': 'virtual', 'option1': 'foo'}, - 'bar': {'type': 'virtual', 'option1': 'foo', 'option2': 'bar'}, - 'baz': {'type': 'virtual', 'option1': 'foo', 'option2': 'bar', 'option3': 'baz'}, + "default": {"type": "virtual"}, + "foo": {"type": "virtual", "option1": "foo"}, + "bar": {"type": "virtual", "option1": "foo", "option2": "bar"}, + "baz": {"type": "virtual", "option1": "foo", "option2": "bar", "option3": "baz"}, } def test_circular_inheritance(self, isolation): with pytest.raises( - ValueError, match='Circular inheritance detected for field `tool.hatch.envs.*.template`: foo -> bar -> foo' + ValueError, match="Circular inheritance detected for field `tool.hatch.envs.*.template`: foo -> bar -> foo" ): _ = ProjectConfig( - isolation, {'envs': {'foo': {'template': 'bar'}, 'bar': {'template': 'foo'}}}, PluginManager() + isolation, {"envs": {"foo": {"template": "bar"}, "bar": {"template": "foo"}}}, PluginManager() ).envs def test_scripts_inheritance(self, isolation): env_config = { - 'default': {'scripts': {'cmd1': 'bar', 'cmd2': 'baz'}}, - 'foo': {'scripts': {'cmd1': 'foo'}}, - 'bar': {'template': 'foo', 'scripts': {'cmd3': 'bar'}}, - 'baz': {}, + "default": {"scripts": {"cmd1": "bar", "cmd2": "baz"}}, + "foo": {"scripts": {"cmd1": "foo"}}, + "bar": {"template": "foo", "scripts": {"cmd3": "bar"}}, + "baz": {}, } - project_config = ProjectConfig(isolation, {'envs': env_config}, PluginManager()) + project_config = ProjectConfig(isolation, {"envs": env_config}, PluginManager()) assert project_config.envs == { - 'default': {'type': 'virtual', 'scripts': {'cmd1': 'bar', 'cmd2': 'baz'}}, - 'foo': {'type': 'virtual', 'scripts': {'cmd1': 'foo', 'cmd2': 'baz'}}, - 'bar': {'type': 'virtual', 'scripts': {'cmd1': 'foo', 'cmd2': 'baz', 'cmd3': 'bar'}}, - 'baz': {'type': 'virtual', 'scripts': {'cmd1': 'bar', 'cmd2': 'baz'}}, + "default": {"type": "virtual", "scripts": {"cmd1": "bar", "cmd2": "baz"}}, + "foo": {"type": "virtual", "scripts": {"cmd1": "foo", "cmd2": "baz"}}, + "bar": {"type": "virtual", "scripts": {"cmd1": "foo", "cmd2": "baz", "cmd3": "bar"}}, + "baz": {"type": "virtual", "scripts": {"cmd1": "bar", "cmd2": "baz"}}, } def test_self_referential(self, isolation): - env_config = {'default': {'option1': 'foo'}, 'bar': {'template': 'bar'}} - project_config = ProjectConfig(isolation, {'envs': env_config}, PluginManager()) + env_config = {"default": {"option1": "foo"}, "bar": {"template": "bar"}} + project_config = ProjectConfig(isolation, {"envs": env_config}, PluginManager()) assert project_config.envs == { - 'default': {'type': 'virtual', 'option1': 'foo'}, - 'bar': {'type': 'virtual'}, + "default": {"type": "virtual", "option1": "foo"}, + "bar": {"type": "virtual"}, } def test_detached(self, isolation): - env_config = {'default': {'option1': 'foo'}, 'bar': {'detached': True}} - project_config = ProjectConfig(isolation, {'envs': env_config}, PluginManager()) + env_config = {"default": {"option1": "foo"}, "bar": {"detached": True}} + project_config = ProjectConfig(isolation, {"envs": env_config}, PluginManager()) assert project_config.envs == { - 'default': {'type': 'virtual', 'option1': 'foo'}, - 'bar': {'type': 'virtual', 'skip-install': True}, + "default": {"type": "virtual", "option1": "foo"}, + "bar": {"type": "virtual", "skip-install": True}, } def test_matrices_not_array(self, isolation): - with pytest.raises(TypeError, match='Field `tool.hatch.envs.foo.matrix` must be an array'): - _ = ProjectConfig(isolation, {'envs': {'foo': {'matrix': 9000}}}, PluginManager()).envs + with pytest.raises(TypeError, match="Field `tool.hatch.envs.foo.matrix` must be an array"): + _ = ProjectConfig(isolation, {"envs": {"foo": {"matrix": 9000}}}, PluginManager()).envs def test_matrix_not_table(self, isolation): - with pytest.raises(TypeError, match='Entry #1 in field `tool.hatch.envs.foo.matrix` must be a table'): - _ = ProjectConfig(isolation, {'envs': {'foo': {'matrix': [9000]}}}, PluginManager()).envs + with pytest.raises(TypeError, match="Entry #1 in field `tool.hatch.envs.foo.matrix` must be a table"): + _ = ProjectConfig(isolation, {"envs": {"foo": {"matrix": [9000]}}}, PluginManager()).envs def test_matrix_empty(self, isolation): - with pytest.raises(ValueError, match='Matrix #1 in field `tool.hatch.envs.foo.matrix` cannot be empty'): - _ = ProjectConfig(isolation, {'envs': {'foo': {'matrix': [{}]}}}, PluginManager()).envs + with pytest.raises(ValueError, match="Matrix #1 in field `tool.hatch.envs.foo.matrix` cannot be empty"): + _ = ProjectConfig(isolation, {"envs": {"foo": {"matrix": [{}]}}}, PluginManager()).envs def test_matrix_variable_empty_string(self, isolation): with pytest.raises( - ValueError, match='Variable #1 in matrix #1 in field `tool.hatch.envs.foo.matrix` cannot be an empty string' + ValueError, match="Variable #1 in matrix #1 in field `tool.hatch.envs.foo.matrix` cannot be an empty string" ): - _ = ProjectConfig(isolation, {'envs': {'foo': {'matrix': [{'': []}]}}}, PluginManager()).envs + _ = ProjectConfig(isolation, {"envs": {"foo": {"matrix": [{"": []}]}}}, PluginManager()).envs def test_matrix_variable_not_array(self, isolation): with pytest.raises( - TypeError, match='Variable `bar` in matrix #1 in field `tool.hatch.envs.foo.matrix` must be an array' + TypeError, match="Variable `bar` in matrix #1 in field `tool.hatch.envs.foo.matrix` must be an array" ): - _ = ProjectConfig(isolation, {'envs': {'foo': {'matrix': [{'bar': 9000}]}}}, PluginManager()).envs + _ = ProjectConfig(isolation, {"envs": {"foo": {"matrix": [{"bar": 9000}]}}}, PluginManager()).envs def test_matrix_variable_array_empty(self, isolation): with pytest.raises( - ValueError, match='Variable `bar` in matrix #1 in field `tool.hatch.envs.foo.matrix` cannot be empty' + ValueError, match="Variable `bar` in matrix #1 in field `tool.hatch.envs.foo.matrix` cannot be empty" ): - _ = ProjectConfig(isolation, {'envs': {'foo': {'matrix': [{'bar': []}]}}}, PluginManager()).envs + _ = ProjectConfig(isolation, {"envs": {"foo": {"matrix": [{"bar": []}]}}}, PluginManager()).envs def test_matrix_variable_entry_not_string(self, isolation): with pytest.raises( TypeError, - match='Value #1 of variable `bar` in matrix #1 in field `tool.hatch.envs.foo.matrix` must be a string', + match="Value #1 of variable `bar` in matrix #1 in field `tool.hatch.envs.foo.matrix` must be a string", ): - _ = ProjectConfig(isolation, {'envs': {'foo': {'matrix': [{'bar': [9000]}]}}}, PluginManager()).envs + _ = ProjectConfig(isolation, {"envs": {"foo": {"matrix": [{"bar": [9000]}]}}}, PluginManager()).envs def test_matrix_variable_entry_empty_string(self, isolation): with pytest.raises( ValueError, match=( - 'Value #1 of variable `bar` in matrix #1 in field `tool.hatch.envs.foo.matrix` ' - 'cannot be an empty string' + "Value #1 of variable `bar` in matrix #1 in field `tool.hatch.envs.foo.matrix` " + "cannot be an empty string" ), ): - _ = ProjectConfig(isolation, {'envs': {'foo': {'matrix': [{'bar': ['']}]}}}, PluginManager()).envs + _ = ProjectConfig(isolation, {"envs": {"foo": {"matrix": [{"bar": [""]}]}}}, PluginManager()).envs def test_matrix_variable_entry_duplicate(self, isolation): with pytest.raises( ValueError, - match='Value #2 of variable `bar` in matrix #1 in field `tool.hatch.envs.foo.matrix` is a duplicate', + match="Value #2 of variable `bar` in matrix #1 in field `tool.hatch.envs.foo.matrix` is a duplicate", ): - _ = ProjectConfig(isolation, {'envs': {'foo': {'matrix': [{'bar': ['1', '1']}]}}}, PluginManager()).envs + _ = ProjectConfig(isolation, {"envs": {"foo": {"matrix": [{"bar": ["1", "1"]}]}}}, PluginManager()).envs def test_matrix_multiple_python_variables(self, isolation): with pytest.raises( ValueError, - match='Matrix #1 in field `tool.hatch.envs.foo.matrix` cannot contain both `py` and `python` variables', + match="Matrix #1 in field `tool.hatch.envs.foo.matrix` cannot contain both `py` and `python` variables", ): _ = ProjectConfig( isolation, - {'envs': {'foo': {'matrix': [{'py': ['39', '310'], 'python': ['39', '311']}]}}}, + {"envs": {"foo": {"matrix": [{"py": ["39", "310"], "python": ["39", "311"]}]}}}, PluginManager(), ).envs def test_matrix_name_format_not_string(self, isolation): - with pytest.raises(TypeError, match='Field `tool.hatch.envs.foo.matrix-name-format` must be a string'): - _ = ProjectConfig(isolation, {'envs': {'foo': {'matrix-name-format': 9000}}}, PluginManager()).envs + with pytest.raises(TypeError, match="Field `tool.hatch.envs.foo.matrix-name-format` must be a string"): + _ = ProjectConfig(isolation, {"envs": {"foo": {"matrix-name-format": 9000}}}, PluginManager()).envs def test_matrix_name_format_invalid(self, isolation): with pytest.raises( ValueError, - match='Field `tool.hatch.envs.foo.matrix-name-format` must contain at least the `{value}` placeholder', + match="Field `tool.hatch.envs.foo.matrix-name-format` must contain at least the `{value}` placeholder", ): - _ = ProjectConfig(isolation, {'envs': {'foo': {'matrix-name-format': 'bar'}}}, PluginManager()).envs + _ = ProjectConfig(isolation, {"envs": {"foo": {"matrix-name-format": "bar"}}}, PluginManager()).envs def test_overrides_not_table(self, isolation): - with pytest.raises(TypeError, match='Field `tool.hatch.envs.foo.overrides` must be a table'): - _ = ProjectConfig(isolation, {'envs': {'foo': {'overrides': 9000}}}, PluginManager()).envs + with pytest.raises(TypeError, match="Field `tool.hatch.envs.foo.overrides` must be a table"): + _ = ProjectConfig(isolation, {"envs": {"foo": {"overrides": 9000}}}, PluginManager()).envs def test_overrides_platform_not_table(self, isolation): - with pytest.raises(TypeError, match='Field `tool.hatch.envs.foo.overrides.platform` must be a table'): - _ = ProjectConfig(isolation, {'envs': {'foo': {'overrides': {'platform': 9000}}}}, PluginManager()).envs + with pytest.raises(TypeError, match="Field `tool.hatch.envs.foo.overrides.platform` must be a table"): + _ = ProjectConfig(isolation, {"envs": {"foo": {"overrides": {"platform": 9000}}}}, PluginManager()).envs def test_overrides_env_not_table(self, isolation): - with pytest.raises(TypeError, match='Field `tool.hatch.envs.foo.overrides.env` must be a table'): - _ = ProjectConfig(isolation, {'envs': {'foo': {'overrides': {'env': 9000}}}}, PluginManager()).envs + with pytest.raises(TypeError, match="Field `tool.hatch.envs.foo.overrides.env` must be a table"): + _ = ProjectConfig(isolation, {"envs": {"foo": {"overrides": {"env": 9000}}}}, PluginManager()).envs def test_overrides_matrix_not_table(self, isolation): - with pytest.raises(TypeError, match='Field `tool.hatch.envs.foo.overrides.matrix` must be a table'): + with pytest.raises(TypeError, match="Field `tool.hatch.envs.foo.overrides.matrix` must be a table"): _ = ProjectConfig( isolation, - {'envs': {'foo': {'matrix': [{'version': ['9000']}], 'overrides': {'matrix': 9000}}}}, + {"envs": {"foo": {"matrix": [{"version": ["9000"]}], "overrides": {"matrix": 9000}}}}, PluginManager(), ).envs def test_overrides_name_not_table(self, isolation): - with pytest.raises(TypeError, match='Field `tool.hatch.envs.foo.overrides.name` must be a table'): + with pytest.raises(TypeError, match="Field `tool.hatch.envs.foo.overrides.name` must be a table"): _ = ProjectConfig( isolation, - {'envs': {'foo': {'matrix': [{'version': ['9000']}], 'overrides': {'name': 9000}}}}, + {"envs": {"foo": {"matrix": [{"version": ["9000"]}], "overrides": {"name": 9000}}}}, PluginManager(), ).envs def test_overrides_platform_entry_not_table(self, isolation): - with pytest.raises(TypeError, match='Field `tool.hatch.envs.foo.overrides.platform.bar` must be a table'): + with pytest.raises(TypeError, match="Field `tool.hatch.envs.foo.overrides.platform.bar` must be a table"): _ = ProjectConfig( - isolation, {'envs': {'foo': {'overrides': {'platform': {'bar': 9000}}}}}, PluginManager() + isolation, {"envs": {"foo": {"overrides": {"platform": {"bar": 9000}}}}}, PluginManager() ).envs def test_overrides_env_entry_not_table(self, isolation): - with pytest.raises(TypeError, match='Field `tool.hatch.envs.foo.overrides.env.bar` must be a table'): - _ = ProjectConfig(isolation, {'envs': {'foo': {'overrides': {'env': {'bar': 9000}}}}}, PluginManager()).envs + with pytest.raises(TypeError, match="Field `tool.hatch.envs.foo.overrides.env.bar` must be a table"): + _ = ProjectConfig(isolation, {"envs": {"foo": {"overrides": {"env": {"bar": 9000}}}}}, PluginManager()).envs def test_overrides_matrix_entry_not_table(self, isolation): - with pytest.raises(TypeError, match='Field `tool.hatch.envs.foo.overrides.matrix.bar` must be a table'): + with pytest.raises(TypeError, match="Field `tool.hatch.envs.foo.overrides.matrix.bar` must be a table"): _ = ProjectConfig( isolation, - {'envs': {'foo': {'matrix': [{'version': ['9000']}], 'overrides': {'matrix': {'bar': 9000}}}}}, + {"envs": {"foo": {"matrix": [{"version": ["9000"]}], "overrides": {"matrix": {"bar": 9000}}}}}, PluginManager(), ).envs def test_overrides_name_entry_not_table(self, isolation): - with pytest.raises(TypeError, match='Field `tool.hatch.envs.foo.overrides.name.bar` must be a table'): + with pytest.raises(TypeError, match="Field `tool.hatch.envs.foo.overrides.name.bar` must be a table"): _ = ProjectConfig( isolation, - {'envs': {'foo': {'matrix': [{'version': ['9000']}], 'overrides': {'name': {'bar': 9000}}}}}, + {"envs": {"foo": {"matrix": [{"version": ["9000"]}], "overrides": {"name": {"bar": 9000}}}}}, PluginManager(), ).envs def test_matrix_simple_no_python(self, isolation): - env_config = {'foo': {'option': True, 'matrix': [{'version': ['9000', '3.14']}]}} - project_config = ProjectConfig(isolation, {'envs': env_config}, PluginManager()) + env_config = {"foo": {"option": True, "matrix": [{"version": ["9000", "3.14"]}]}} + project_config = ProjectConfig(isolation, {"envs": env_config}, PluginManager()) expected_envs = { - 'default': {'type': 'virtual'}, - 'foo.9000': {'type': 'virtual', 'option': True}, - 'foo.3.14': {'type': 'virtual', 'option': True}, + "default": {"type": "virtual"}, + "foo.9000": {"type": "virtual", "option": True}, + "foo.3.14": {"type": "virtual", "option": True}, } assert project_config.envs == expected_envs - assert project_config.matrices['foo'] == construct_matrix_data('foo', env_config) + assert project_config.matrices["foo"] == construct_matrix_data("foo", env_config) def test_matrix_simple_no_python_custom_name_format(self, isolation): env_config = { - 'foo': { - 'option': True, - 'matrix-name-format': '{variable}_{value}', - 'matrix': [{'version': ['9000', '3.14']}], + "foo": { + "option": True, + "matrix-name-format": "{variable}_{value}", + "matrix": [{"version": ["9000", "3.14"]}], } } - project_config = ProjectConfig(isolation, {'envs': env_config}, PluginManager()) + project_config = ProjectConfig(isolation, {"envs": env_config}, PluginManager()) expected_envs = { - 'default': {'type': 'virtual'}, - 'foo.version_9000': {'type': 'virtual', 'option': True}, - 'foo.version_3.14': {'type': 'virtual', 'option': True}, + "default": {"type": "virtual"}, + "foo.version_9000": {"type": "virtual", "option": True}, + "foo.version_3.14": {"type": "virtual", "option": True}, } assert project_config.envs == expected_envs - assert project_config.matrices['foo'] == construct_matrix_data('foo', env_config) + assert project_config.matrices["foo"] == construct_matrix_data("foo", env_config) - @pytest.mark.parametrize('indicator', ['py', 'python']) + @pytest.mark.parametrize("indicator", ["py", "python"]) def test_matrix_simple_only_python(self, isolation, indicator): - env_config = {'foo': {'option': True, 'matrix': [{indicator: ['39', '310']}]}} - project_config = ProjectConfig(isolation, {'envs': env_config}, PluginManager()) + env_config = {"foo": {"option": True, "matrix": [{indicator: ["39", "310"]}]}} + project_config = ProjectConfig(isolation, {"envs": env_config}, PluginManager()) expected_envs = { - 'default': {'type': 'virtual'}, - 'foo.py39': {'type': 'virtual', 'option': True, 'python': '39'}, - 'foo.py310': {'type': 'virtual', 'option': True, 'python': '310'}, + "default": {"type": "virtual"}, + "foo.py39": {"type": "virtual", "option": True, "python": "39"}, + "foo.py310": {"type": "virtual", "option": True, "python": "310"}, } assert project_config.envs == expected_envs - assert project_config.matrices['foo'] == construct_matrix_data('foo', env_config) + assert project_config.matrices["foo"] == construct_matrix_data("foo", env_config) - @pytest.mark.parametrize('indicator', ['py', 'python']) + @pytest.mark.parametrize("indicator", ["py", "python"]) def test_matrix_simple(self, isolation, indicator): - env_config = {'foo': {'option': True, 'matrix': [{'version': ['9000', '3.14'], indicator: ['39', '310']}]}} - project_config = ProjectConfig(isolation, {'envs': env_config}, PluginManager()) + env_config = {"foo": {"option": True, "matrix": [{"version": ["9000", "3.14"], indicator: ["39", "310"]}]}} + project_config = ProjectConfig(isolation, {"envs": env_config}, PluginManager()) expected_envs = { - 'default': {'type': 'virtual'}, - 'foo.py39-9000': {'type': 'virtual', 'option': True, 'python': '39'}, - 'foo.py39-3.14': {'type': 'virtual', 'option': True, 'python': '39'}, - 'foo.py310-9000': {'type': 'virtual', 'option': True, 'python': '310'}, - 'foo.py310-3.14': {'type': 'virtual', 'option': True, 'python': '310'}, + "default": {"type": "virtual"}, + "foo.py39-9000": {"type": "virtual", "option": True, "python": "39"}, + "foo.py39-3.14": {"type": "virtual", "option": True, "python": "39"}, + "foo.py310-9000": {"type": "virtual", "option": True, "python": "310"}, + "foo.py310-3.14": {"type": "virtual", "option": True, "python": "310"}, } assert project_config.envs == expected_envs - assert project_config.matrices['foo'] == construct_matrix_data('foo', env_config) + assert project_config.matrices["foo"] == construct_matrix_data("foo", env_config) - @pytest.mark.parametrize('indicator', ['py', 'python']) + @pytest.mark.parametrize("indicator", ["py", "python"]) def test_matrix_simple_custom_name_format(self, isolation, indicator): env_config = { - 'foo': { - 'option': True, - 'matrix-name-format': '{variable}_{value}', - 'matrix': [{'version': ['9000', '3.14'], indicator: ['39', '310']}], + "foo": { + "option": True, + "matrix-name-format": "{variable}_{value}", + "matrix": [{"version": ["9000", "3.14"], indicator: ["39", "310"]}], } } - project_config = ProjectConfig(isolation, {'envs': env_config}, PluginManager()) + project_config = ProjectConfig(isolation, {"envs": env_config}, PluginManager()) expected_envs = { - 'default': {'type': 'virtual'}, - 'foo.py39-version_9000': {'type': 'virtual', 'option': True, 'python': '39'}, - 'foo.py39-version_3.14': {'type': 'virtual', 'option': True, 'python': '39'}, - 'foo.py310-version_9000': {'type': 'virtual', 'option': True, 'python': '310'}, - 'foo.py310-version_3.14': {'type': 'virtual', 'option': True, 'python': '310'}, + "default": {"type": "virtual"}, + "foo.py39-version_9000": {"type": "virtual", "option": True, "python": "39"}, + "foo.py39-version_3.14": {"type": "virtual", "option": True, "python": "39"}, + "foo.py310-version_9000": {"type": "virtual", "option": True, "python": "310"}, + "foo.py310-version_3.14": {"type": "virtual", "option": True, "python": "310"}, } assert project_config.envs == expected_envs - assert project_config.matrices['foo'] == construct_matrix_data('foo', env_config) + assert project_config.matrices["foo"] == construct_matrix_data("foo", env_config) def test_matrix_multiple_non_python(self, isolation): env_config = { - 'foo': { - 'option': True, - 'matrix': [{'version': ['9000', '3.14'], 'py': ['39', '310'], 'foo': ['baz', 'bar']}], + "foo": { + "option": True, + "matrix": [{"version": ["9000", "3.14"], "py": ["39", "310"], "foo": ["baz", "bar"]}], } } - project_config = ProjectConfig(isolation, {'envs': env_config}, PluginManager()) + project_config = ProjectConfig(isolation, {"envs": env_config}, PluginManager()) expected_envs = { - 'default': {'type': 'virtual'}, - 'foo.py39-9000-baz': {'type': 'virtual', 'option': True, 'python': '39'}, - 'foo.py39-9000-bar': {'type': 'virtual', 'option': True, 'python': '39'}, - 'foo.py39-3.14-baz': {'type': 'virtual', 'option': True, 'python': '39'}, - 'foo.py39-3.14-bar': {'type': 'virtual', 'option': True, 'python': '39'}, - 'foo.py310-9000-baz': {'type': 'virtual', 'option': True, 'python': '310'}, - 'foo.py310-9000-bar': {'type': 'virtual', 'option': True, 'python': '310'}, - 'foo.py310-3.14-baz': {'type': 'virtual', 'option': True, 'python': '310'}, - 'foo.py310-3.14-bar': {'type': 'virtual', 'option': True, 'python': '310'}, + "default": {"type": "virtual"}, + "foo.py39-9000-baz": {"type": "virtual", "option": True, "python": "39"}, + "foo.py39-9000-bar": {"type": "virtual", "option": True, "python": "39"}, + "foo.py39-3.14-baz": {"type": "virtual", "option": True, "python": "39"}, + "foo.py39-3.14-bar": {"type": "virtual", "option": True, "python": "39"}, + "foo.py310-9000-baz": {"type": "virtual", "option": True, "python": "310"}, + "foo.py310-9000-bar": {"type": "virtual", "option": True, "python": "310"}, + "foo.py310-3.14-baz": {"type": "virtual", "option": True, "python": "310"}, + "foo.py310-3.14-bar": {"type": "virtual", "option": True, "python": "310"}, } assert project_config.envs == expected_envs - assert project_config.matrices['foo'] == construct_matrix_data('foo', env_config) + assert project_config.matrices["foo"] == construct_matrix_data("foo", env_config) def test_matrix_series(self, isolation): env_config = { - 'foo': { - 'option': True, - 'matrix': [ - {'version': ['9000', '3.14'], 'py': ['39', '310'], 'foo': ['baz', 'bar']}, - {'version': ['9000'], 'py': ['310'], 'baz': ['foo', 'test'], 'bar': ['foobar']}, + "foo": { + "option": True, + "matrix": [ + {"version": ["9000", "3.14"], "py": ["39", "310"], "foo": ["baz", "bar"]}, + {"version": ["9000"], "py": ["310"], "baz": ["foo", "test"], "bar": ["foobar"]}, ], } } - project_config = ProjectConfig(isolation, {'envs': env_config}, PluginManager()) + project_config = ProjectConfig(isolation, {"envs": env_config}, PluginManager()) expected_envs = { - 'default': {'type': 'virtual'}, - 'foo.py39-9000-baz': {'type': 'virtual', 'option': True, 'python': '39'}, - 'foo.py39-9000-bar': {'type': 'virtual', 'option': True, 'python': '39'}, - 'foo.py39-3.14-baz': {'type': 'virtual', 'option': True, 'python': '39'}, - 'foo.py39-3.14-bar': {'type': 'virtual', 'option': True, 'python': '39'}, - 'foo.py310-9000-baz': {'type': 'virtual', 'option': True, 'python': '310'}, - 'foo.py310-9000-bar': {'type': 'virtual', 'option': True, 'python': '310'}, - 'foo.py310-3.14-baz': {'type': 'virtual', 'option': True, 'python': '310'}, - 'foo.py310-3.14-bar': {'type': 'virtual', 'option': True, 'python': '310'}, - 'foo.py310-9000-foo-foobar': {'type': 'virtual', 'option': True, 'python': '310'}, - 'foo.py310-9000-test-foobar': {'type': 'virtual', 'option': True, 'python': '310'}, + "default": {"type": "virtual"}, + "foo.py39-9000-baz": {"type": "virtual", "option": True, "python": "39"}, + "foo.py39-9000-bar": {"type": "virtual", "option": True, "python": "39"}, + "foo.py39-3.14-baz": {"type": "virtual", "option": True, "python": "39"}, + "foo.py39-3.14-bar": {"type": "virtual", "option": True, "python": "39"}, + "foo.py310-9000-baz": {"type": "virtual", "option": True, "python": "310"}, + "foo.py310-9000-bar": {"type": "virtual", "option": True, "python": "310"}, + "foo.py310-3.14-baz": {"type": "virtual", "option": True, "python": "310"}, + "foo.py310-3.14-bar": {"type": "virtual", "option": True, "python": "310"}, + "foo.py310-9000-foo-foobar": {"type": "virtual", "option": True, "python": "310"}, + "foo.py310-9000-test-foobar": {"type": "virtual", "option": True, "python": "310"}, } assert project_config.envs == expected_envs - assert project_config.matrices['foo'] == construct_matrix_data('foo', env_config) + assert project_config.matrices["foo"] == construct_matrix_data("foo", env_config) def test_matrices_not_inherited(self, isolation): env_config = { - 'foo': {'option1': True, 'matrix': [{'py': ['39']}]}, - 'bar': {'template': 'foo', 'option2': False}, + "foo": {"option1": True, "matrix": [{"py": ["39"]}]}, + "bar": {"template": "foo", "option2": False}, } - project_config = ProjectConfig(isolation, {'envs': env_config}, PluginManager()) + project_config = ProjectConfig(isolation, {"envs": env_config}, PluginManager()) expected_envs = { - 'default': {'type': 'virtual'}, - 'foo.py39': {'type': 'virtual', 'option1': True, 'python': '39'}, - 'bar': {'type': 'virtual', 'option1': True, 'option2': False}, + "default": {"type": "virtual"}, + "foo.py39": {"type": "virtual", "option1": True, "python": "39"}, + "bar": {"type": "virtual", "option1": True, "option2": False}, } assert project_config.envs == expected_envs - assert project_config.matrices['foo'] == construct_matrix_data('foo', env_config) + assert project_config.matrices["foo"] == construct_matrix_data("foo", env_config) def test_matrix_default_naming(self, isolation): - env_config = {'default': {'option': True, 'matrix': [{'version': ['9000', '3.14']}]}} - project_config = ProjectConfig(isolation, {'envs': env_config}, PluginManager()) + env_config = {"default": {"option": True, "matrix": [{"version": ["9000", "3.14"]}]}} + project_config = ProjectConfig(isolation, {"envs": env_config}, PluginManager()) expected_envs = { - '9000': {'type': 'virtual', 'option': True}, - '3.14': {'type': 'virtual', 'option': True}, + "9000": {"type": "virtual", "option": True}, + "3.14": {"type": "virtual", "option": True}, } assert project_config.envs == expected_envs - assert project_config.matrices['default'] == construct_matrix_data('default', env_config) + assert project_config.matrices["default"] == construct_matrix_data("default", env_config) def test_matrix_pypy_naming(self, isolation): - env_config = {'foo': {'option': True, 'matrix': [{'py': ['python3.9', 'pypy3']}]}} - project_config = ProjectConfig(isolation, {'envs': env_config}, PluginManager()) + env_config = {"foo": {"option": True, "matrix": [{"py": ["python3.9", "pypy3"]}]}} + project_config = ProjectConfig(isolation, {"envs": env_config}, PluginManager()) expected_envs = { - 'default': {'type': 'virtual'}, - 'foo.python3.9': {'type': 'virtual', 'option': True, 'python': 'python3.9'}, - 'foo.pypy3': {'type': 'virtual', 'option': True, 'python': 'pypy3'}, + "default": {"type": "virtual"}, + "foo.python3.9": {"type": "virtual", "option": True, "python": "python3.9"}, + "foo.pypy3": {"type": "virtual", "option": True, "python": "pypy3"}, } assert project_config.envs == expected_envs - assert project_config.matrices['foo'] == construct_matrix_data('foo', env_config) + assert project_config.matrices["foo"] == construct_matrix_data("foo", env_config) - @pytest.mark.parametrize('option', MAPPING_OPTIONS) + @pytest.mark.parametrize("option", MAPPING_OPTIONS) def test_overrides_matrix_mapping_invalid_type(self, isolation, option): with pytest.raises( TypeError, - match=f'Field `tool.hatch.envs.foo.overrides.matrix.version.{option}` must be a string or an array', + match=f"Field `tool.hatch.envs.foo.overrides.matrix.version.{option}` must be a string or an array", ): _ = ProjectConfig( isolation, { - 'envs': { - 'foo': {'matrix': [{'version': ['9000']}], 'overrides': {'matrix': {'version': {option: 9000}}}} + "envs": { + "foo": {"matrix": [{"version": ["9000"]}], "overrides": {"matrix": {"version": {option: 9000}}}} } }, PluginManager(), ).envs - @pytest.mark.parametrize('option', MAPPING_OPTIONS) + @pytest.mark.parametrize("option", MAPPING_OPTIONS) def test_overrides_matrix_mapping_array_entry_invalid_type(self, isolation, option): with pytest.raises( TypeError, match=( - f'Entry #1 in field `tool.hatch.envs.foo.overrides.matrix.version.{option}` ' - f'must be a string or an inline table' + f"Entry #1 in field `tool.hatch.envs.foo.overrides.matrix.version.{option}` " + f"must be a string or an inline table" ), ): _ = ProjectConfig( isolation, { - 'envs': { - 'foo': { - 'matrix': [{'version': ['9000']}], - 'overrides': {'matrix': {'version': {option: [9000]}}}, + "envs": { + "foo": { + "matrix": [{"version": ["9000"]}], + "overrides": {"matrix": {"version": {option: [9000]}}}, } } }, PluginManager(), ).envs - @pytest.mark.parametrize('option', MAPPING_OPTIONS) + @pytest.mark.parametrize("option", MAPPING_OPTIONS) def test_overrides_matrix_mapping_table_entry_no_key(self, isolation, option): with pytest.raises( ValueError, match=( - f'Entry #1 in field `tool.hatch.envs.foo.overrides.matrix.version.{option}` ' - f'must have an option named `key`' + f"Entry #1 in field `tool.hatch.envs.foo.overrides.matrix.version.{option}` " + f"must have an option named `key`" ), ): _ = ProjectConfig( isolation, { - 'envs': { - 'foo': {'matrix': [{'version': ['9000']}], 'overrides': {'matrix': {'version': {option: [{}]}}}} + "envs": { + "foo": {"matrix": [{"version": ["9000"]}], "overrides": {"matrix": {"version": {option: [{}]}}}} } }, PluginManager(), ).envs - @pytest.mark.parametrize('option', MAPPING_OPTIONS) + @pytest.mark.parametrize("option", MAPPING_OPTIONS) def test_overrides_matrix_mapping_table_entry_key_not_string(self, isolation, option): with pytest.raises( TypeError, match=( - f'Option `key` in entry #1 in field `tool.hatch.envs.foo.overrides.matrix.version.{option}` ' - f'must be a string' + f"Option `key` in entry #1 in field `tool.hatch.envs.foo.overrides.matrix.version.{option}` " + f"must be a string" ), ): _ = ProjectConfig( isolation, { - 'envs': { - 'foo': { - 'matrix': [{'version': ['9000']}], - 'overrides': {'matrix': {'version': {option: [{'key': 9000}]}}}, + "envs": { + "foo": { + "matrix": [{"version": ["9000"]}], + "overrides": {"matrix": {"version": {option: [{"key": 9000}]}}}, } } }, PluginManager(), ).envs - @pytest.mark.parametrize('option', MAPPING_OPTIONS) + @pytest.mark.parametrize("option", MAPPING_OPTIONS) def test_overrides_matrix_mapping_table_entry_key_empty_string(self, isolation, option): with pytest.raises( ValueError, match=( - f'Option `key` in entry #1 in field `tool.hatch.envs.foo.overrides.matrix.version.{option}` ' - f'cannot be an empty string' + f"Option `key` in entry #1 in field `tool.hatch.envs.foo.overrides.matrix.version.{option}` " + f"cannot be an empty string" ), ): _ = ProjectConfig( isolation, { - 'envs': { - 'foo': { - 'matrix': [{'version': ['9000']}], - 'overrides': {'matrix': {'version': {option: [{'key': ''}]}}}, + "envs": { + "foo": { + "matrix": [{"version": ["9000"]}], + "overrides": {"matrix": {"version": {option: [{"key": ""}]}}}, } } }, PluginManager(), ).envs - @pytest.mark.parametrize('option', MAPPING_OPTIONS) + @pytest.mark.parametrize("option", MAPPING_OPTIONS) def test_overrides_matrix_mapping_table_entry_value_not_string(self, isolation, option): with pytest.raises( TypeError, match=( - f'Option `value` in entry #1 in field `tool.hatch.envs.foo.overrides.matrix.version.{option}` ' - f'must be a string' + f"Option `value` in entry #1 in field `tool.hatch.envs.foo.overrides.matrix.version.{option}` " + f"must be a string" ), ): _ = ProjectConfig( isolation, { - 'envs': { - 'foo': { - 'matrix': [{'version': ['9000']}], - 'overrides': {'matrix': {'version': {option: [{'key': 'foo', 'value': 9000}]}}}, + "envs": { + "foo": { + "matrix": [{"version": ["9000"]}], + "overrides": {"matrix": {"version": {option: [{"key": "foo", "value": 9000}]}}}, } } }, PluginManager(), ).envs - @pytest.mark.parametrize('option', MAPPING_OPTIONS) + @pytest.mark.parametrize("option", MAPPING_OPTIONS) def test_overrides_matrix_mapping_table_entry_if_not_array(self, isolation, option): with pytest.raises( TypeError, match=( - f'Option `if` in entry #1 in field `tool.hatch.envs.foo.overrides.matrix.version.{option}` ' - f'must be an array' + f"Option `if` in entry #1 in field `tool.hatch.envs.foo.overrides.matrix.version.{option}` " + f"must be an array" ), ): _ = ProjectConfig( isolation, { - 'envs': { - 'foo': { - 'matrix': [{'version': ['9000']}], - 'overrides': { - 'matrix': {'version': {option: [{'key': 'foo', 'value': 'bar', 'if': 9000}]}} + "envs": { + "foo": { + "matrix": [{"version": ["9000"]}], + "overrides": { + "matrix": {"version": {option: [{"key": "foo", "value": "bar", "if": 9000}]}} }, } } @@ -674,1009 +674,1009 @@ def test_overrides_matrix_mapping_table_entry_if_not_array(self, isolation, opti PluginManager(), ).envs - @pytest.mark.parametrize('option', ARRAY_OPTIONS) + @pytest.mark.parametrize("option", ARRAY_OPTIONS) def test_overrides_matrix_array_invalid_type(self, isolation, option): with pytest.raises( - TypeError, match=f'Field `tool.hatch.envs.foo.overrides.matrix.version.{option}` must be an array' + TypeError, match=f"Field `tool.hatch.envs.foo.overrides.matrix.version.{option}` must be an array" ): _ = ProjectConfig( isolation, { - 'envs': { - 'foo': {'matrix': [{'version': ['9000']}], 'overrides': {'matrix': {'version': {option: 9000}}}} + "envs": { + "foo": {"matrix": [{"version": ["9000"]}], "overrides": {"matrix": {"version": {option: 9000}}}} } }, PluginManager(), ).envs - @pytest.mark.parametrize('option', ARRAY_OPTIONS) + @pytest.mark.parametrize("option", ARRAY_OPTIONS) def test_overrides_matrix_array_table_entry_no_value(self, isolation, option): with pytest.raises( ValueError, match=( - f'Entry #1 in field `tool.hatch.envs.foo.overrides.matrix.version.{option}` ' - f'must have an option named `value`' + f"Entry #1 in field `tool.hatch.envs.foo.overrides.matrix.version.{option}` " + f"must have an option named `value`" ), ): _ = ProjectConfig( isolation, { - 'envs': { - 'foo': {'matrix': [{'version': ['9000']}], 'overrides': {'matrix': {'version': {option: [{}]}}}} + "envs": { + "foo": {"matrix": [{"version": ["9000"]}], "overrides": {"matrix": {"version": {option: [{}]}}}} } }, PluginManager(), ).envs - @pytest.mark.parametrize('option', ARRAY_OPTIONS) + @pytest.mark.parametrize("option", ARRAY_OPTIONS) def test_overrides_matrix_array_table_entry_value_not_string(self, isolation, option): with pytest.raises( TypeError, match=( - f'Option `value` in entry #1 in field `tool.hatch.envs.foo.overrides.matrix.version.{option}` ' - f'must be a string' + f"Option `value` in entry #1 in field `tool.hatch.envs.foo.overrides.matrix.version.{option}` " + f"must be a string" ), ): _ = ProjectConfig( isolation, { - 'envs': { - 'foo': { - 'matrix': [{'version': ['9000']}], - 'overrides': {'matrix': {'version': {option: [{'value': 9000}]}}}, + "envs": { + "foo": { + "matrix": [{"version": ["9000"]}], + "overrides": {"matrix": {"version": {option: [{"value": 9000}]}}}, } } }, PluginManager(), ).envs - @pytest.mark.parametrize('option', ARRAY_OPTIONS) + @pytest.mark.parametrize("option", ARRAY_OPTIONS) def test_overrides_matrix_array_table_entry_value_empty_string(self, isolation, option): with pytest.raises( ValueError, match=( - f'Option `value` in entry #1 in field `tool.hatch.envs.foo.overrides.matrix.version.{option}` ' - f'cannot be an empty string' + f"Option `value` in entry #1 in field `tool.hatch.envs.foo.overrides.matrix.version.{option}` " + f"cannot be an empty string" ), ): _ = ProjectConfig( isolation, { - 'envs': { - 'foo': { - 'matrix': [{'version': ['9000']}], - 'overrides': {'matrix': {'version': {option: [{'value': ''}]}}}, + "envs": { + "foo": { + "matrix": [{"version": ["9000"]}], + "overrides": {"matrix": {"version": {option: [{"value": ""}]}}}, } } }, PluginManager(), ).envs - @pytest.mark.parametrize('option', ARRAY_OPTIONS) + @pytest.mark.parametrize("option", ARRAY_OPTIONS) def test_overrides_matrix_array_table_entry_if_not_array(self, isolation, option): with pytest.raises( TypeError, match=( - f'Option `if` in entry #1 in field `tool.hatch.envs.foo.overrides.matrix.version.{option}` ' - f'must be an array' + f"Option `if` in entry #1 in field `tool.hatch.envs.foo.overrides.matrix.version.{option}` " + f"must be an array" ), ): _ = ProjectConfig( isolation, { - 'envs': { - 'foo': { - 'matrix': [{'version': ['9000']}], - 'overrides': {'matrix': {'version': {option: [{'value': 'foo', 'if': 9000}]}}}, + "envs": { + "foo": { + "matrix": [{"version": ["9000"]}], + "overrides": {"matrix": {"version": {option: [{"value": "foo", "if": 9000}]}}}, } } }, PluginManager(), ).envs - @pytest.mark.parametrize('option', ARRAY_OPTIONS) + @pytest.mark.parametrize("option", ARRAY_OPTIONS) def test_overrides_matrix_array_entry_invalid_type(self, isolation, option): with pytest.raises( TypeError, match=( - f'Entry #1 in field `tool.hatch.envs.foo.overrides.matrix.version.{option}` ' - f'must be a string or an inline table' + f"Entry #1 in field `tool.hatch.envs.foo.overrides.matrix.version.{option}` " + f"must be a string or an inline table" ), ): _ = ProjectConfig( isolation, { - 'envs': { - 'foo': { - 'matrix': [{'version': ['9000']}], - 'overrides': {'matrix': {'version': {option: [9000]}}}, + "envs": { + "foo": { + "matrix": [{"version": ["9000"]}], + "overrides": {"matrix": {"version": {option: [9000]}}}, } } }, PluginManager(), ).envs - @pytest.mark.parametrize('option', STRING_OPTIONS) + @pytest.mark.parametrize("option", STRING_OPTIONS) def test_overrides_matrix_string_invalid_type(self, isolation, option): with pytest.raises( TypeError, match=( - f'Field `tool.hatch.envs.foo.overrides.matrix.version.{option}` ' - f'must be a string, inline table, or an array' + f"Field `tool.hatch.envs.foo.overrides.matrix.version.{option}` " + f"must be a string, inline table, or an array" ), ): _ = ProjectConfig( isolation, { - 'envs': { - 'foo': {'matrix': [{'version': ['9000']}], 'overrides': {'matrix': {'version': {option: 9000}}}} + "envs": { + "foo": {"matrix": [{"version": ["9000"]}], "overrides": {"matrix": {"version": {option: 9000}}}} } }, PluginManager(), ).envs - @pytest.mark.parametrize('option', STRING_OPTIONS) + @pytest.mark.parametrize("option", STRING_OPTIONS) def test_overrides_matrix_string_table_no_value(self, isolation, option): with pytest.raises( ValueError, - match=f'Field `tool.hatch.envs.foo.overrides.matrix.version.{option}` must have an option named `value`', + match=f"Field `tool.hatch.envs.foo.overrides.matrix.version.{option}` must have an option named `value`", ): _ = ProjectConfig( isolation, { - 'envs': { - 'foo': {'matrix': [{'version': ['9000']}], 'overrides': {'matrix': {'version': {option: {}}}}} + "envs": { + "foo": {"matrix": [{"version": ["9000"]}], "overrides": {"matrix": {"version": {option: {}}}}} } }, PluginManager(), ).envs - @pytest.mark.parametrize('option', STRING_OPTIONS) + @pytest.mark.parametrize("option", STRING_OPTIONS) def test_overrides_matrix_string_table_value_not_string(self, isolation, option): with pytest.raises( TypeError, - match=f'Option `value` in field `tool.hatch.envs.foo.overrides.matrix.version.{option}` must be a string', + match=f"Option `value` in field `tool.hatch.envs.foo.overrides.matrix.version.{option}` must be a string", ): _ = ProjectConfig( isolation, { - 'envs': { - 'foo': { - 'matrix': [{'version': ['9000']}], - 'overrides': {'matrix': {'version': {option: {'value': 9000}}}}, + "envs": { + "foo": { + "matrix": [{"version": ["9000"]}], + "overrides": {"matrix": {"version": {option: {"value": 9000}}}}, } } }, PluginManager(), ).envs - @pytest.mark.parametrize('option', STRING_OPTIONS) + @pytest.mark.parametrize("option", STRING_OPTIONS) def test_overrides_matrix_string_array_entry_invalid_type(self, isolation, option): with pytest.raises( TypeError, match=( - f'Entry #1 in field `tool.hatch.envs.foo.overrides.matrix.version.{option}` ' - f'must be a string or an inline table' + f"Entry #1 in field `tool.hatch.envs.foo.overrides.matrix.version.{option}` " + f"must be a string or an inline table" ), ): _ = ProjectConfig( isolation, { - 'envs': { - 'foo': { - 'matrix': [{'version': ['9000']}], - 'overrides': {'matrix': {'version': {option: [9000]}}}, + "envs": { + "foo": { + "matrix": [{"version": ["9000"]}], + "overrides": {"matrix": {"version": {option: [9000]}}}, } } }, PluginManager(), ).envs - @pytest.mark.parametrize('option', STRING_OPTIONS) + @pytest.mark.parametrize("option", STRING_OPTIONS) def test_overrides_matrix_string_array_table_no_value(self, isolation, option): with pytest.raises( ValueError, match=( - f'Entry #1 in field `tool.hatch.envs.foo.overrides.matrix.version.{option}` ' - f'must have an option named `value`' + f"Entry #1 in field `tool.hatch.envs.foo.overrides.matrix.version.{option}` " + f"must have an option named `value`" ), ): _ = ProjectConfig( isolation, { - 'envs': { - 'foo': {'matrix': [{'version': ['9000']}], 'overrides': {'matrix': {'version': {option: [{}]}}}} + "envs": { + "foo": {"matrix": [{"version": ["9000"]}], "overrides": {"matrix": {"version": {option: [{}]}}}} } }, PluginManager(), ).envs - @pytest.mark.parametrize('option', STRING_OPTIONS) + @pytest.mark.parametrize("option", STRING_OPTIONS) def test_overrides_matrix_string_array_table_value_not_string(self, isolation, option): with pytest.raises( TypeError, match=( - f'Option `value` in entry #1 in field `tool.hatch.envs.foo.overrides.matrix.version.{option}` ' - f'must be a string' + f"Option `value` in entry #1 in field `tool.hatch.envs.foo.overrides.matrix.version.{option}` " + f"must be a string" ), ): _ = ProjectConfig( isolation, { - 'envs': { - 'foo': { - 'matrix': [{'version': ['9000']}], - 'overrides': {'matrix': {'version': {option: [{'value': 9000}]}}}, + "envs": { + "foo": { + "matrix": [{"version": ["9000"]}], + "overrides": {"matrix": {"version": {option: [{"value": 9000}]}}}, } } }, PluginManager(), ).envs - @pytest.mark.parametrize('option', STRING_OPTIONS) + @pytest.mark.parametrize("option", STRING_OPTIONS) def test_overrides_matrix_string_array_table_if_not_array(self, isolation, option): with pytest.raises( TypeError, match=( - f'Option `if` in entry #1 in field `tool.hatch.envs.foo.overrides.matrix.version.{option}` ' - f'must be an array' + f"Option `if` in entry #1 in field `tool.hatch.envs.foo.overrides.matrix.version.{option}` " + f"must be an array" ), ): _ = ProjectConfig( isolation, { - 'envs': { - 'foo': { - 'matrix': [{'version': ['9000']}], - 'overrides': {'matrix': {'version': {option: [{'value': 'foo', 'if': 9000}]}}}, + "envs": { + "foo": { + "matrix": [{"version": ["9000"]}], + "overrides": {"matrix": {"version": {option: [{"value": "foo", "if": 9000}]}}}, } } }, PluginManager(), ).envs - @pytest.mark.parametrize('option', BOOLEAN_OPTIONS) + @pytest.mark.parametrize("option", BOOLEAN_OPTIONS) def test_overrides_matrix_boolean_invalid_type(self, isolation, option): with pytest.raises( TypeError, match=( - f'Field `tool.hatch.envs.foo.overrides.matrix.version.{option}` ' - f'must be a boolean, inline table, or an array' + f"Field `tool.hatch.envs.foo.overrides.matrix.version.{option}` " + f"must be a boolean, inline table, or an array" ), ): _ = ProjectConfig( isolation, { - 'envs': { - 'foo': {'matrix': [{'version': ['9000']}], 'overrides': {'matrix': {'version': {option: 9000}}}} + "envs": { + "foo": {"matrix": [{"version": ["9000"]}], "overrides": {"matrix": {"version": {option: 9000}}}} } }, PluginManager(), ).envs - @pytest.mark.parametrize('option', BOOLEAN_OPTIONS) + @pytest.mark.parametrize("option", BOOLEAN_OPTIONS) def test_overrides_matrix_boolean_table_no_value(self, isolation, option): with pytest.raises( ValueError, - match=f'Field `tool.hatch.envs.foo.overrides.matrix.version.{option}` must have an option named `value`', + match=f"Field `tool.hatch.envs.foo.overrides.matrix.version.{option}` must have an option named `value`", ): _ = ProjectConfig( isolation, { - 'envs': { - 'foo': {'matrix': [{'version': ['9000']}], 'overrides': {'matrix': {'version': {option: {}}}}} + "envs": { + "foo": {"matrix": [{"version": ["9000"]}], "overrides": {"matrix": {"version": {option: {}}}}} } }, PluginManager(), ).envs - @pytest.mark.parametrize('option', BOOLEAN_OPTIONS) + @pytest.mark.parametrize("option", BOOLEAN_OPTIONS) def test_overrides_matrix_boolean_table_value_not_boolean(self, isolation, option): with pytest.raises( TypeError, - match=f'Option `value` in field `tool.hatch.envs.foo.overrides.matrix.version.{option}` must be a boolean', + match=f"Option `value` in field `tool.hatch.envs.foo.overrides.matrix.version.{option}` must be a boolean", ): _ = ProjectConfig( isolation, { - 'envs': { - 'foo': { - 'matrix': [{'version': ['9000']}], - 'overrides': {'matrix': {'version': {option: {'value': 9000}}}}, + "envs": { + "foo": { + "matrix": [{"version": ["9000"]}], + "overrides": {"matrix": {"version": {option: {"value": 9000}}}}, } } }, PluginManager(), ).envs - @pytest.mark.parametrize('option', BOOLEAN_OPTIONS) + @pytest.mark.parametrize("option", BOOLEAN_OPTIONS) def test_overrides_matrix_boolean_array_entry_invalid_type(self, isolation, option): with pytest.raises( TypeError, match=( - f'Entry #1 in field `tool.hatch.envs.foo.overrides.matrix.version.{option}` ' - f'must be a boolean or an inline table' + f"Entry #1 in field `tool.hatch.envs.foo.overrides.matrix.version.{option}` " + f"must be a boolean or an inline table" ), ): _ = ProjectConfig( isolation, { - 'envs': { - 'foo': { - 'matrix': [{'version': ['9000']}], - 'overrides': {'matrix': {'version': {option: [9000]}}}, + "envs": { + "foo": { + "matrix": [{"version": ["9000"]}], + "overrides": {"matrix": {"version": {option: [9000]}}}, } } }, PluginManager(), ).envs - @pytest.mark.parametrize('option', BOOLEAN_OPTIONS) + @pytest.mark.parametrize("option", BOOLEAN_OPTIONS) def test_overrides_matrix_boolean_array_table_no_value(self, isolation, option): with pytest.raises( ValueError, match=( - f'Entry #1 in field `tool.hatch.envs.foo.overrides.matrix.version.{option}` ' - f'must have an option named `value`' + f"Entry #1 in field `tool.hatch.envs.foo.overrides.matrix.version.{option}` " + f"must have an option named `value`" ), ): _ = ProjectConfig( isolation, { - 'envs': { - 'foo': {'matrix': [{'version': ['9000']}], 'overrides': {'matrix': {'version': {option: [{}]}}}} + "envs": { + "foo": {"matrix": [{"version": ["9000"]}], "overrides": {"matrix": {"version": {option: [{}]}}}} } }, PluginManager(), ).envs - @pytest.mark.parametrize('option', BOOLEAN_OPTIONS) + @pytest.mark.parametrize("option", BOOLEAN_OPTIONS) def test_overrides_matrix_boolean_array_table_value_not_boolean(self, isolation, option): with pytest.raises( TypeError, match=( - f'Option `value` in entry #1 in field `tool.hatch.envs.foo.overrides.matrix.version.{option}` ' - f'must be a boolean' + f"Option `value` in entry #1 in field `tool.hatch.envs.foo.overrides.matrix.version.{option}` " + f"must be a boolean" ), ): _ = ProjectConfig( isolation, { - 'envs': { - 'foo': { - 'matrix': [{'version': ['9000']}], - 'overrides': {'matrix': {'version': {option: [{'value': 9000}]}}}, + "envs": { + "foo": { + "matrix": [{"version": ["9000"]}], + "overrides": {"matrix": {"version": {option: [{"value": 9000}]}}}, } } }, PluginManager(), ).envs - @pytest.mark.parametrize('option', BOOLEAN_OPTIONS) + @pytest.mark.parametrize("option", BOOLEAN_OPTIONS) def test_overrides_matrix_boolean_array_table_if_not_array(self, isolation, option): with pytest.raises( TypeError, match=( - f'Option `if` in entry #1 in field `tool.hatch.envs.foo.overrides.matrix.version.{option}` ' - f'must be an array' + f"Option `if` in entry #1 in field `tool.hatch.envs.foo.overrides.matrix.version.{option}` " + f"must be an array" ), ): _ = ProjectConfig( isolation, { - 'envs': { - 'foo': { - 'matrix': [{'version': ['9000']}], - 'overrides': {'matrix': {'version': {option: [{'value': True, 'if': 9000}]}}}, + "envs": { + "foo": { + "matrix": [{"version": ["9000"]}], + "overrides": {"matrix": {"version": {option: [{"value": True, "if": 9000}]}}}, } } }, PluginManager(), ).envs - @pytest.mark.parametrize('option', BOOLEAN_OPTIONS) + @pytest.mark.parametrize("option", BOOLEAN_OPTIONS) def test_overrides_matrix_boolean_array_table_platform_not_array(self, isolation, option): with pytest.raises( TypeError, match=( - f'Option `platform` in entry #1 in field `tool.hatch.envs.foo.overrides.matrix.version.{option}` ' - f'must be an array' + f"Option `platform` in entry #1 in field `tool.hatch.envs.foo.overrides.matrix.version.{option}` " + f"must be an array" ), ): _ = ProjectConfig( isolation, { - 'envs': { - 'foo': { - 'matrix': [{'version': ['9000']}], - 'overrides': {'matrix': {'version': {option: [{'value': True, 'platform': 9000}]}}}, + "envs": { + "foo": { + "matrix": [{"version": ["9000"]}], + "overrides": {"matrix": {"version": {option: [{"value": True, "platform": 9000}]}}}, } } }, PluginManager(), ).envs - @pytest.mark.parametrize('option', BOOLEAN_OPTIONS) + @pytest.mark.parametrize("option", BOOLEAN_OPTIONS) def test_overrides_matrix_boolean_array_table_platform_item_not_string(self, isolation, option): with pytest.raises( TypeError, match=( - f'Item #1 in option `platform` in entry #1 in field ' - f'`tool.hatch.envs.foo.overrides.matrix.version.{option}` must be a string' + f"Item #1 in option `platform` in entry #1 in field " + f"`tool.hatch.envs.foo.overrides.matrix.version.{option}` must be a string" ), ): _ = ProjectConfig( isolation, { - 'envs': { - 'foo': { - 'matrix': [{'version': ['9000']}], - 'overrides': {'matrix': {'version': {option: [{'value': True, 'platform': [9000]}]}}}, + "envs": { + "foo": { + "matrix": [{"version": ["9000"]}], + "overrides": {"matrix": {"version": {option: [{"value": True, "platform": [9000]}]}}}, } } }, PluginManager(), ).envs - @pytest.mark.parametrize('option', BOOLEAN_OPTIONS) + @pytest.mark.parametrize("option", BOOLEAN_OPTIONS) def test_overrides_matrix_boolean_array_table_env_not_array(self, isolation, option): with pytest.raises( TypeError, match=( - f'Option `env` in entry #1 in field `tool.hatch.envs.foo.overrides.matrix.version.{option}` ' - f'must be an array' + f"Option `env` in entry #1 in field `tool.hatch.envs.foo.overrides.matrix.version.{option}` " + f"must be an array" ), ): _ = ProjectConfig( isolation, { - 'envs': { - 'foo': { - 'matrix': [{'version': ['9000']}], - 'overrides': {'matrix': {'version': {option: [{'value': True, 'env': 9000}]}}}, + "envs": { + "foo": { + "matrix": [{"version": ["9000"]}], + "overrides": {"matrix": {"version": {option: [{"value": True, "env": 9000}]}}}, } } }, PluginManager(), ).envs - @pytest.mark.parametrize('option', BOOLEAN_OPTIONS) + @pytest.mark.parametrize("option", BOOLEAN_OPTIONS) def test_overrides_matrix_boolean_array_table_env_item_not_string(self, isolation, option): with pytest.raises( TypeError, match=( - f'Item #1 in option `env` in entry #1 in field ' - f'`tool.hatch.envs.foo.overrides.matrix.version.{option}` must be a string' + f"Item #1 in option `env` in entry #1 in field " + f"`tool.hatch.envs.foo.overrides.matrix.version.{option}` must be a string" ), ): _ = ProjectConfig( isolation, { - 'envs': { - 'foo': { - 'matrix': [{'version': ['9000']}], - 'overrides': {'matrix': {'version': {option: [{'value': True, 'env': [9000]}]}}}, + "envs": { + "foo": { + "matrix": [{"version": ["9000"]}], + "overrides": {"matrix": {"version": {option: [{"value": True, "env": [9000]}]}}}, } } }, PluginManager(), ).envs - @pytest.mark.parametrize('option', MAPPING_OPTIONS) + @pytest.mark.parametrize("option", MAPPING_OPTIONS) def test_overrides_matrix_mapping_string_with_value(self, isolation, option): env_config = { - 'foo': { - 'matrix': [{'version': ['9000']}, {'feature': ['bar']}], - 'overrides': {'matrix': {'version': {option: 'FOO=ok'}}}, + "foo": { + "matrix": [{"version": ["9000"]}, {"feature": ["bar"]}], + "overrides": {"matrix": {"version": {option: "FOO=ok"}}}, } } - project_config = ProjectConfig(isolation, {'envs': env_config}, PluginManager()) + project_config = ProjectConfig(isolation, {"envs": env_config}, PluginManager()) expected_envs = { - 'default': {'type': 'virtual'}, - 'foo.9000': {'type': 'virtual', option: {'FOO': 'ok'}}, - 'foo.bar': {'type': 'virtual'}, + "default": {"type": "virtual"}, + "foo.9000": {"type": "virtual", option: {"FOO": "ok"}}, + "foo.bar": {"type": "virtual"}, } assert project_config.envs == expected_envs - assert project_config.matrices['foo'] == construct_matrix_data('foo', env_config) + assert project_config.matrices["foo"] == construct_matrix_data("foo", env_config) - @pytest.mark.parametrize('option', MAPPING_OPTIONS) + @pytest.mark.parametrize("option", MAPPING_OPTIONS) def test_overrides_matrix_mapping_string_without_value(self, isolation, option): env_config = { - 'foo': { - 'matrix': [{'version': ['9000']}, {'feature': ['bar']}], - 'overrides': {'matrix': {'version': {option: 'FOO'}}}, + "foo": { + "matrix": [{"version": ["9000"]}, {"feature": ["bar"]}], + "overrides": {"matrix": {"version": {option: "FOO"}}}, } } - project_config = ProjectConfig(isolation, {'envs': env_config}, PluginManager()) + project_config = ProjectConfig(isolation, {"envs": env_config}, PluginManager()) expected_envs = { - 'default': {'type': 'virtual'}, - 'foo.9000': {'type': 'virtual', option: {'FOO': '9000'}}, - 'foo.bar': {'type': 'virtual'}, + "default": {"type": "virtual"}, + "foo.9000": {"type": "virtual", option: {"FOO": "9000"}}, + "foo.bar": {"type": "virtual"}, } assert project_config.envs == expected_envs - assert project_config.matrices['foo'] == construct_matrix_data('foo', env_config) + assert project_config.matrices["foo"] == construct_matrix_data("foo", env_config) - @pytest.mark.parametrize('option', MAPPING_OPTIONS) + @pytest.mark.parametrize("option", MAPPING_OPTIONS) def test_overrides_matrix_mapping_string_override(self, isolation, option): env_config = { - 'foo': { - option: {'TEST': 'baz'}, - 'matrix': [{'version': ['9000']}, {'feature': ['bar']}], - 'overrides': {'matrix': {'version': {option: 'TEST'}}}, + "foo": { + option: {"TEST": "baz"}, + "matrix": [{"version": ["9000"]}, {"feature": ["bar"]}], + "overrides": {"matrix": {"version": {option: "TEST"}}}, } } - project_config = ProjectConfig(isolation, {'envs': env_config}, PluginManager()) + project_config = ProjectConfig(isolation, {"envs": env_config}, PluginManager()) expected_envs = { - 'default': {'type': 'virtual'}, - 'foo.9000': {'type': 'virtual', option: {'TEST': '9000'}}, - 'foo.bar': {'type': 'virtual', option: {'TEST': 'baz'}}, + "default": {"type": "virtual"}, + "foo.9000": {"type": "virtual", option: {"TEST": "9000"}}, + "foo.bar": {"type": "virtual", option: {"TEST": "baz"}}, } assert project_config.envs == expected_envs - assert project_config.matrices['foo'] == construct_matrix_data('foo', env_config) + assert project_config.matrices["foo"] == construct_matrix_data("foo", env_config) - @pytest.mark.parametrize('option', MAPPING_OPTIONS) + @pytest.mark.parametrize("option", MAPPING_OPTIONS) def test_overrides_matrix_mapping_array_string_with_value(self, isolation, option): env_config = { - 'foo': { - 'matrix': [{'version': ['9000']}, {'feature': ['bar']}], - 'overrides': {'matrix': {'version': {option: ['FOO=ok']}}}, + "foo": { + "matrix": [{"version": ["9000"]}, {"feature": ["bar"]}], + "overrides": {"matrix": {"version": {option: ["FOO=ok"]}}}, } } - project_config = ProjectConfig(isolation, {'envs': env_config}, PluginManager()) + project_config = ProjectConfig(isolation, {"envs": env_config}, PluginManager()) expected_envs = { - 'default': {'type': 'virtual'}, - 'foo.9000': {'type': 'virtual', option: {'FOO': 'ok'}}, - 'foo.bar': {'type': 'virtual'}, + "default": {"type": "virtual"}, + "foo.9000": {"type": "virtual", option: {"FOO": "ok"}}, + "foo.bar": {"type": "virtual"}, } assert project_config.envs == expected_envs - assert project_config.matrices['foo'] == construct_matrix_data('foo', env_config) + assert project_config.matrices["foo"] == construct_matrix_data("foo", env_config) - @pytest.mark.parametrize('option', MAPPING_OPTIONS) + @pytest.mark.parametrize("option", MAPPING_OPTIONS) def test_overrides_matrix_mapping_array_string_without_value(self, isolation, option): env_config = { - 'foo': { - 'matrix': [{'version': ['9000']}, {'feature': ['bar']}], - 'overrides': {'matrix': {'version': {option: ['FOO']}}}, + "foo": { + "matrix": [{"version": ["9000"]}, {"feature": ["bar"]}], + "overrides": {"matrix": {"version": {option: ["FOO"]}}}, } } - project_config = ProjectConfig(isolation, {'envs': env_config}, PluginManager()) + project_config = ProjectConfig(isolation, {"envs": env_config}, PluginManager()) expected_envs = { - 'default': {'type': 'virtual'}, - 'foo.9000': {'type': 'virtual', option: {'FOO': '9000'}}, - 'foo.bar': {'type': 'virtual'}, + "default": {"type": "virtual"}, + "foo.9000": {"type": "virtual", option: {"FOO": "9000"}}, + "foo.bar": {"type": "virtual"}, } assert project_config.envs == expected_envs - assert project_config.matrices['foo'] == construct_matrix_data('foo', env_config) + assert project_config.matrices["foo"] == construct_matrix_data("foo", env_config) - @pytest.mark.parametrize('option', MAPPING_OPTIONS) + @pytest.mark.parametrize("option", MAPPING_OPTIONS) def test_overrides_matrix_mapping_array_string_override(self, isolation, option): env_config = { - 'foo': { - option: {'TEST': 'baz'}, - 'matrix': [{'version': ['9000']}, {'feature': ['bar']}], - 'overrides': {'matrix': {'version': {option: ['TEST']}}}, + "foo": { + option: {"TEST": "baz"}, + "matrix": [{"version": ["9000"]}, {"feature": ["bar"]}], + "overrides": {"matrix": {"version": {option: ["TEST"]}}}, } } - project_config = ProjectConfig(isolation, {'envs': env_config}, PluginManager()) + project_config = ProjectConfig(isolation, {"envs": env_config}, PluginManager()) expected_envs = { - 'default': {'type': 'virtual'}, - 'foo.9000': {'type': 'virtual', option: {'TEST': '9000'}}, - 'foo.bar': {'type': 'virtual', option: {'TEST': 'baz'}}, + "default": {"type": "virtual"}, + "foo.9000": {"type": "virtual", option: {"TEST": "9000"}}, + "foo.bar": {"type": "virtual", option: {"TEST": "baz"}}, } assert project_config.envs == expected_envs - assert project_config.matrices['foo'] == construct_matrix_data('foo', env_config) + assert project_config.matrices["foo"] == construct_matrix_data("foo", env_config) - @pytest.mark.parametrize('option', MAPPING_OPTIONS) + @pytest.mark.parametrize("option", MAPPING_OPTIONS) def test_overrides_matrix_mapping_array_table_key_with_value(self, isolation, option): env_config = { - 'foo': { - 'matrix': [{'version': ['9000']}, {'feature': ['bar']}], - 'overrides': {'matrix': {'version': {option: [{'key': 'FOO', 'value': 'ok'}]}}}, + "foo": { + "matrix": [{"version": ["9000"]}, {"feature": ["bar"]}], + "overrides": {"matrix": {"version": {option: [{"key": "FOO", "value": "ok"}]}}}, } } - project_config = ProjectConfig(isolation, {'envs': env_config}, PluginManager()) + project_config = ProjectConfig(isolation, {"envs": env_config}, PluginManager()) expected_envs = { - 'default': {'type': 'virtual'}, - 'foo.9000': {'type': 'virtual', option: {'FOO': 'ok'}}, - 'foo.bar': {'type': 'virtual'}, + "default": {"type": "virtual"}, + "foo.9000": {"type": "virtual", option: {"FOO": "ok"}}, + "foo.bar": {"type": "virtual"}, } assert project_config.envs == expected_envs - assert project_config.matrices['foo'] == construct_matrix_data('foo', env_config) + assert project_config.matrices["foo"] == construct_matrix_data("foo", env_config) - @pytest.mark.parametrize('option', MAPPING_OPTIONS) + @pytest.mark.parametrize("option", MAPPING_OPTIONS) def test_overrides_matrix_mapping_array_table_key_without_value(self, isolation, option): env_config = { - 'foo': { - 'matrix': [{'version': ['9000']}, {'feature': ['bar']}], - 'overrides': {'matrix': {'version': {option: [{'key': 'FOO'}]}}}, + "foo": { + "matrix": [{"version": ["9000"]}, {"feature": ["bar"]}], + "overrides": {"matrix": {"version": {option: [{"key": "FOO"}]}}}, } } - project_config = ProjectConfig(isolation, {'envs': env_config}, PluginManager()) + project_config = ProjectConfig(isolation, {"envs": env_config}, PluginManager()) expected_envs = { - 'default': {'type': 'virtual'}, - 'foo.9000': {'type': 'virtual', option: {'FOO': '9000'}}, - 'foo.bar': {'type': 'virtual'}, + "default": {"type": "virtual"}, + "foo.9000": {"type": "virtual", option: {"FOO": "9000"}}, + "foo.bar": {"type": "virtual"}, } assert project_config.envs == expected_envs - assert project_config.matrices['foo'] == construct_matrix_data('foo', env_config) + assert project_config.matrices["foo"] == construct_matrix_data("foo", env_config) - @pytest.mark.parametrize('option', MAPPING_OPTIONS) + @pytest.mark.parametrize("option", MAPPING_OPTIONS) def test_overrides_matrix_mapping_array_table_override(self, isolation, option): env_config = { - 'foo': { - option: {'TEST': 'baz'}, - 'matrix': [{'version': ['9000']}, {'feature': ['bar']}], - 'overrides': {'matrix': {'version': {option: [{'key': 'TEST'}]}}}, + "foo": { + option: {"TEST": "baz"}, + "matrix": [{"version": ["9000"]}, {"feature": ["bar"]}], + "overrides": {"matrix": {"version": {option: [{"key": "TEST"}]}}}, } } - project_config = ProjectConfig(isolation, {'envs': env_config}, PluginManager()) + project_config = ProjectConfig(isolation, {"envs": env_config}, PluginManager()) expected_envs = { - 'default': {'type': 'virtual'}, - 'foo.9000': {'type': 'virtual', option: {'TEST': '9000'}}, - 'foo.bar': {'type': 'virtual', option: {'TEST': 'baz'}}, + "default": {"type": "virtual"}, + "foo.9000": {"type": "virtual", option: {"TEST": "9000"}}, + "foo.bar": {"type": "virtual", option: {"TEST": "baz"}}, } assert project_config.envs == expected_envs - assert project_config.matrices['foo'] == construct_matrix_data('foo', env_config) + assert project_config.matrices["foo"] == construct_matrix_data("foo", env_config) - @pytest.mark.parametrize('option', MAPPING_OPTIONS) + @pytest.mark.parametrize("option", MAPPING_OPTIONS) def test_overrides_matrix_mapping_array_table_conditional(self, isolation, option): env_config = { - 'foo': { - option: {'TEST': 'baz'}, - 'matrix': [{'version': ['9000', '42']}, {'feature': ['bar']}], - 'overrides': {'matrix': {'version': {option: [{'key': 'TEST', 'if': ['42']}]}}}, + "foo": { + option: {"TEST": "baz"}, + "matrix": [{"version": ["9000", "42"]}, {"feature": ["bar"]}], + "overrides": {"matrix": {"version": {option: [{"key": "TEST", "if": ["42"]}]}}}, } } - project_config = ProjectConfig(isolation, {'envs': env_config}, PluginManager()) + project_config = ProjectConfig(isolation, {"envs": env_config}, PluginManager()) expected_envs = { - 'default': {'type': 'virtual'}, - 'foo.9000': {'type': 'virtual', option: {'TEST': 'baz'}}, - 'foo.42': {'type': 'virtual', option: {'TEST': '42'}}, - 'foo.bar': {'type': 'virtual', option: {'TEST': 'baz'}}, + "default": {"type": "virtual"}, + "foo.9000": {"type": "virtual", option: {"TEST": "baz"}}, + "foo.42": {"type": "virtual", option: {"TEST": "42"}}, + "foo.bar": {"type": "virtual", option: {"TEST": "baz"}}, } assert project_config.envs == expected_envs - assert project_config.matrices['foo'] == construct_matrix_data('foo', env_config) + assert project_config.matrices["foo"] == construct_matrix_data("foo", env_config) - @pytest.mark.parametrize('option', MAPPING_OPTIONS) + @pytest.mark.parametrize("option", MAPPING_OPTIONS) def test_overrides_matrix_mapping_overwrite(self, isolation, option): env_config = { - 'foo': { - option: {'TEST': 'baz'}, - 'matrix': [{'version': ['9000']}, {'feature': ['bar']}], - 'overrides': {'matrix': {'version': {f'set-{option}': ['FOO=bar', {'key': 'BAZ'}]}}}, + "foo": { + option: {"TEST": "baz"}, + "matrix": [{"version": ["9000"]}, {"feature": ["bar"]}], + "overrides": {"matrix": {"version": {f"set-{option}": ["FOO=bar", {"key": "BAZ"}]}}}, } } - project_config = ProjectConfig(isolation, {'envs': env_config}, PluginManager()) + project_config = ProjectConfig(isolation, {"envs": env_config}, PluginManager()) expected_envs = { - 'default': {'type': 'virtual'}, - 'foo.9000': {'type': 'virtual', option: {'FOO': 'bar', 'BAZ': '9000'}}, - 'foo.bar': {'type': 'virtual', option: {'TEST': 'baz'}}, + "default": {"type": "virtual"}, + "foo.9000": {"type": "virtual", option: {"FOO": "bar", "BAZ": "9000"}}, + "foo.bar": {"type": "virtual", option: {"TEST": "baz"}}, } assert project_config.envs == expected_envs - assert project_config.matrices['foo'] == construct_matrix_data('foo', env_config) + assert project_config.matrices["foo"] == construct_matrix_data("foo", env_config) - @pytest.mark.parametrize('option', ARRAY_OPTIONS) + @pytest.mark.parametrize("option", ARRAY_OPTIONS) def test_overrides_matrix_array_string(self, isolation, option): env_config = { - 'foo': { - 'matrix': [{'version': ['9000']}, {'feature': ['bar']}], - 'overrides': {'matrix': {'version': {option: ['run foo']}}}, + "foo": { + "matrix": [{"version": ["9000"]}, {"feature": ["bar"]}], + "overrides": {"matrix": {"version": {option: ["run foo"]}}}, } } - project_config = ProjectConfig(isolation, {'envs': env_config}, PluginManager()) + project_config = ProjectConfig(isolation, {"envs": env_config}, PluginManager()) expected_envs = { - 'default': {'type': 'virtual'}, - 'foo.9000': {'type': 'virtual', option: ['run foo']}, - 'foo.bar': {'type': 'virtual'}, + "default": {"type": "virtual"}, + "foo.9000": {"type": "virtual", option: ["run foo"]}, + "foo.bar": {"type": "virtual"}, } assert project_config.envs == expected_envs - assert project_config.matrices['foo'] == construct_matrix_data('foo', env_config) + assert project_config.matrices["foo"] == construct_matrix_data("foo", env_config) - @pytest.mark.parametrize('option', ARRAY_OPTIONS) + @pytest.mark.parametrize("option", ARRAY_OPTIONS) def test_overrides_matrix_array_string_existing_append(self, isolation, option): env_config = { - 'foo': { - option: ['run baz'], - 'matrix': [{'version': ['9000']}, {'feature': ['bar']}], - 'overrides': {'matrix': {'version': {option: ['run foo']}}}, + "foo": { + option: ["run baz"], + "matrix": [{"version": ["9000"]}, {"feature": ["bar"]}], + "overrides": {"matrix": {"version": {option: ["run foo"]}}}, } } - project_config = ProjectConfig(isolation, {'envs': env_config}, PluginManager()) + project_config = ProjectConfig(isolation, {"envs": env_config}, PluginManager()) expected_envs = { - 'default': {'type': 'virtual'}, - 'foo.9000': {'type': 'virtual', option: ['run baz', 'run foo']}, - 'foo.bar': {'type': 'virtual', option: ['run baz']}, + "default": {"type": "virtual"}, + "foo.9000": {"type": "virtual", option: ["run baz", "run foo"]}, + "foo.bar": {"type": "virtual", option: ["run baz"]}, } assert project_config.envs == expected_envs - assert project_config.matrices['foo'] == construct_matrix_data('foo', env_config) + assert project_config.matrices["foo"] == construct_matrix_data("foo", env_config) - @pytest.mark.parametrize('option', ARRAY_OPTIONS) + @pytest.mark.parametrize("option", ARRAY_OPTIONS) def test_overrides_matrix_array_table(self, isolation, option): env_config = { - 'foo': { - 'matrix': [{'version': ['9000']}, {'feature': ['bar']}], - 'overrides': {'matrix': {'version': {option: [{'value': 'run foo'}]}}}, + "foo": { + "matrix": [{"version": ["9000"]}, {"feature": ["bar"]}], + "overrides": {"matrix": {"version": {option: [{"value": "run foo"}]}}}, } } - project_config = ProjectConfig(isolation, {'envs': env_config}, PluginManager()) + project_config = ProjectConfig(isolation, {"envs": env_config}, PluginManager()) expected_envs = { - 'default': {'type': 'virtual'}, - 'foo.9000': {'type': 'virtual', option: ['run foo']}, - 'foo.bar': {'type': 'virtual'}, + "default": {"type": "virtual"}, + "foo.9000": {"type": "virtual", option: ["run foo"]}, + "foo.bar": {"type": "virtual"}, } assert project_config.envs == expected_envs - assert project_config.matrices['foo'] == construct_matrix_data('foo', env_config) + assert project_config.matrices["foo"] == construct_matrix_data("foo", env_config) - @pytest.mark.parametrize('option', ARRAY_OPTIONS) + @pytest.mark.parametrize("option", ARRAY_OPTIONS) def test_overrides_matrix_array_table_existing_append(self, isolation, option): env_config = { - 'foo': { - option: ['run baz'], - 'matrix': [{'version': ['9000']}, {'feature': ['bar']}], - 'overrides': {'matrix': {'version': {option: [{'value': 'run foo'}]}}}, + "foo": { + option: ["run baz"], + "matrix": [{"version": ["9000"]}, {"feature": ["bar"]}], + "overrides": {"matrix": {"version": {option: [{"value": "run foo"}]}}}, } } - project_config = ProjectConfig(isolation, {'envs': env_config}, PluginManager()) + project_config = ProjectConfig(isolation, {"envs": env_config}, PluginManager()) expected_envs = { - 'default': {'type': 'virtual'}, - 'foo.9000': {'type': 'virtual', option: ['run baz', 'run foo']}, - 'foo.bar': {'type': 'virtual', option: ['run baz']}, + "default": {"type": "virtual"}, + "foo.9000": {"type": "virtual", option: ["run baz", "run foo"]}, + "foo.bar": {"type": "virtual", option: ["run baz"]}, } assert project_config.envs == expected_envs - assert project_config.matrices['foo'] == construct_matrix_data('foo', env_config) + assert project_config.matrices["foo"] == construct_matrix_data("foo", env_config) - @pytest.mark.parametrize('option', ARRAY_OPTIONS) + @pytest.mark.parametrize("option", ARRAY_OPTIONS) def test_overrides_matrix_array_table_conditional(self, isolation, option): env_config = { - 'foo': { - option: ['run baz'], - 'matrix': [{'version': ['9000', '42']}, {'feature': ['bar']}], - 'overrides': {'matrix': {'version': {option: [{'value': 'run foo', 'if': ['42']}]}}}, + "foo": { + option: ["run baz"], + "matrix": [{"version": ["9000", "42"]}, {"feature": ["bar"]}], + "overrides": {"matrix": {"version": {option: [{"value": "run foo", "if": ["42"]}]}}}, } } - project_config = ProjectConfig(isolation, {'envs': env_config}, PluginManager()) + project_config = ProjectConfig(isolation, {"envs": env_config}, PluginManager()) expected_envs = { - 'default': {'type': 'virtual'}, - 'foo.9000': {'type': 'virtual', option: ['run baz']}, - 'foo.42': {'type': 'virtual', option: ['run baz', 'run foo']}, - 'foo.bar': {'type': 'virtual', option: ['run baz']}, + "default": {"type": "virtual"}, + "foo.9000": {"type": "virtual", option: ["run baz"]}, + "foo.42": {"type": "virtual", option: ["run baz", "run foo"]}, + "foo.bar": {"type": "virtual", option: ["run baz"]}, } assert project_config.envs == expected_envs - assert project_config.matrices['foo'] == construct_matrix_data('foo', env_config) + assert project_config.matrices["foo"] == construct_matrix_data("foo", env_config) - @pytest.mark.parametrize('option', ARRAY_OPTIONS) + @pytest.mark.parametrize("option", ARRAY_OPTIONS) def test_overrides_matrix_array_table_conditional_with_platform(self, isolation, option, current_platform): env_config = { - 'foo': { - option: ['run baz'], - 'matrix': [{'version': ['9000', '42']}, {'feature': ['bar']}], - 'overrides': { - 'matrix': { - 'version': {option: [{'value': 'run foo', 'if': ['42'], 'platform': [current_platform]}]} + "foo": { + option: ["run baz"], + "matrix": [{"version": ["9000", "42"]}, {"feature": ["bar"]}], + "overrides": { + "matrix": { + "version": {option: [{"value": "run foo", "if": ["42"], "platform": [current_platform]}]} }, }, } } - project_config = ProjectConfig(isolation, {'envs': env_config}, PluginManager()) + project_config = ProjectConfig(isolation, {"envs": env_config}, PluginManager()) expected_envs = { - 'default': {'type': 'virtual'}, - 'foo.9000': {'type': 'virtual', option: ['run baz']}, - 'foo.42': {'type': 'virtual', option: ['run baz', 'run foo']}, - 'foo.bar': {'type': 'virtual', option: ['run baz']}, + "default": {"type": "virtual"}, + "foo.9000": {"type": "virtual", option: ["run baz"]}, + "foo.42": {"type": "virtual", option: ["run baz", "run foo"]}, + "foo.bar": {"type": "virtual", option: ["run baz"]}, } assert project_config.envs == expected_envs - assert project_config.matrices['foo'] == construct_matrix_data('foo', env_config) + assert project_config.matrices["foo"] == construct_matrix_data("foo", env_config) - @pytest.mark.parametrize('option', ARRAY_OPTIONS) + @pytest.mark.parametrize("option", ARRAY_OPTIONS) def test_overrides_matrix_array_table_conditional_with_wrong_platform(self, isolation, option): env_config = { - 'foo': { - option: ['run baz'], - 'matrix': [{'version': ['9000', '42']}, {'feature': ['bar']}], - 'overrides': { - 'matrix': {'version': {option: [{'value': 'run foo', 'if': ['42'], 'platform': ['bar']}]}}, + "foo": { + option: ["run baz"], + "matrix": [{"version": ["9000", "42"]}, {"feature": ["bar"]}], + "overrides": { + "matrix": {"version": {option: [{"value": "run foo", "if": ["42"], "platform": ["bar"]}]}}, }, } } - project_config = ProjectConfig(isolation, {'envs': env_config}, PluginManager()) + project_config = ProjectConfig(isolation, {"envs": env_config}, PluginManager()) expected_envs = { - 'default': {'type': 'virtual'}, - 'foo.9000': {'type': 'virtual', option: ['run baz']}, - 'foo.42': {'type': 'virtual', option: ['run baz']}, - 'foo.bar': {'type': 'virtual', option: ['run baz']}, + "default": {"type": "virtual"}, + "foo.9000": {"type": "virtual", option: ["run baz"]}, + "foo.42": {"type": "virtual", option: ["run baz"]}, + "foo.bar": {"type": "virtual", option: ["run baz"]}, } assert project_config.envs == expected_envs - assert project_config.matrices['foo'] == construct_matrix_data('foo', env_config) + assert project_config.matrices["foo"] == construct_matrix_data("foo", env_config) - @pytest.mark.parametrize('option', ARRAY_OPTIONS) + @pytest.mark.parametrize("option", ARRAY_OPTIONS) def test_overrides_matrix_array_table_conditional_with_env_var_match(self, isolation, option): - env_var = 'OVERRIDES_ENV_FOO' + env_var = "OVERRIDES_ENV_FOO" env_config = { - 'foo': { - option: ['run baz'], - 'matrix': [{'version': ['9000', '42']}, {'feature': ['bar']}], - 'overrides': { - 'matrix': {'version': {option: [{'value': 'run foo', 'if': ['42'], 'env': [f'{env_var}=bar']}]}} + "foo": { + option: ["run baz"], + "matrix": [{"version": ["9000", "42"]}, {"feature": ["bar"]}], + "overrides": { + "matrix": {"version": {option: [{"value": "run foo", "if": ["42"], "env": [f"{env_var}=bar"]}]}} }, } } - project_config = ProjectConfig(isolation, {'envs': env_config}, PluginManager()) + project_config = ProjectConfig(isolation, {"envs": env_config}, PluginManager()) expected_envs = { - 'default': {'type': 'virtual'}, - 'foo.9000': {'type': 'virtual', option: ['run baz']}, - 'foo.42': {'type': 'virtual', option: ['run baz', 'run foo']}, - 'foo.bar': {'type': 'virtual', option: ['run baz']}, + "default": {"type": "virtual"}, + "foo.9000": {"type": "virtual", option: ["run baz"]}, + "foo.42": {"type": "virtual", option: ["run baz", "run foo"]}, + "foo.bar": {"type": "virtual", option: ["run baz"]}, } - with EnvVars({env_var: 'bar'}): + with EnvVars({env_var: "bar"}): assert project_config.envs == expected_envs - assert project_config.matrices['foo'] == construct_matrix_data('foo', env_config) + assert project_config.matrices["foo"] == construct_matrix_data("foo", env_config) - @pytest.mark.parametrize('option', ARRAY_OPTIONS) + @pytest.mark.parametrize("option", ARRAY_OPTIONS) def test_overrides_matrix_array_table_conditional_with_env_var_match_empty_string(self, isolation, option): - env_var = 'OVERRIDES_ENV_FOO' + env_var = "OVERRIDES_ENV_FOO" env_config = { - 'foo': { - option: ['run baz'], - 'matrix': [{'version': ['9000', '42']}, {'feature': ['bar']}], - 'overrides': { - 'matrix': {'version': {option: [{'value': 'run foo', 'if': ['42'], 'env': [f'{env_var}=']}]}} + "foo": { + option: ["run baz"], + "matrix": [{"version": ["9000", "42"]}, {"feature": ["bar"]}], + "overrides": { + "matrix": {"version": {option: [{"value": "run foo", "if": ["42"], "env": [f"{env_var}="]}]}} }, } } - project_config = ProjectConfig(isolation, {'envs': env_config}, PluginManager()) + project_config = ProjectConfig(isolation, {"envs": env_config}, PluginManager()) expected_envs = { - 'default': {'type': 'virtual'}, - 'foo.9000': {'type': 'virtual', option: ['run baz']}, - 'foo.42': {'type': 'virtual', option: ['run baz', 'run foo']}, - 'foo.bar': {'type': 'virtual', option: ['run baz']}, + "default": {"type": "virtual"}, + "foo.9000": {"type": "virtual", option: ["run baz"]}, + "foo.42": {"type": "virtual", option: ["run baz", "run foo"]}, + "foo.bar": {"type": "virtual", option: ["run baz"]}, } - with EnvVars({env_var: ''}): + with EnvVars({env_var: ""}): assert project_config.envs == expected_envs - assert project_config.matrices['foo'] == construct_matrix_data('foo', env_config) + assert project_config.matrices["foo"] == construct_matrix_data("foo", env_config) - @pytest.mark.parametrize('option', ARRAY_OPTIONS) + @pytest.mark.parametrize("option", ARRAY_OPTIONS) def test_overrides_matrix_array_table_conditional_with_env_var_present(self, isolation, option): - env_var = 'OVERRIDES_ENV_FOO' + env_var = "OVERRIDES_ENV_FOO" env_config = { - 'foo': { - option: ['run baz'], - 'matrix': [{'version': ['9000', '42']}, {'feature': ['bar']}], - 'overrides': {'matrix': {'version': {option: [{'value': 'run foo', 'if': ['42'], 'env': [env_var]}]}}}, + "foo": { + option: ["run baz"], + "matrix": [{"version": ["9000", "42"]}, {"feature": ["bar"]}], + "overrides": {"matrix": {"version": {option: [{"value": "run foo", "if": ["42"], "env": [env_var]}]}}}, } } - project_config = ProjectConfig(isolation, {'envs': env_config}, PluginManager()) + project_config = ProjectConfig(isolation, {"envs": env_config}, PluginManager()) expected_envs = { - 'default': {'type': 'virtual'}, - 'foo.9000': {'type': 'virtual', option: ['run baz']}, - 'foo.42': {'type': 'virtual', option: ['run baz', 'run foo']}, - 'foo.bar': {'type': 'virtual', option: ['run baz']}, + "default": {"type": "virtual"}, + "foo.9000": {"type": "virtual", option: ["run baz"]}, + "foo.42": {"type": "virtual", option: ["run baz", "run foo"]}, + "foo.bar": {"type": "virtual", option: ["run baz"]}, } - with EnvVars({env_var: 'any'}): + with EnvVars({env_var: "any"}): assert project_config.envs == expected_envs - assert project_config.matrices['foo'] == construct_matrix_data('foo', env_config) + assert project_config.matrices["foo"] == construct_matrix_data("foo", env_config) - @pytest.mark.parametrize('option', ARRAY_OPTIONS) + @pytest.mark.parametrize("option", ARRAY_OPTIONS) def test_overrides_matrix_array_table_conditional_with_env_var_no_match(self, isolation, option): - env_var = 'OVERRIDES_ENV_FOO' + env_var = "OVERRIDES_ENV_FOO" env_config = { - 'foo': { - option: ['run baz'], - 'matrix': [{'version': ['9000', '42']}, {'feature': ['bar']}], - 'overrides': { - 'matrix': {'version': {option: [{'value': 'run foo', 'if': ['42'], 'env': [f'{env_var}=bar']}]}} + "foo": { + option: ["run baz"], + "matrix": [{"version": ["9000", "42"]}, {"feature": ["bar"]}], + "overrides": { + "matrix": {"version": {option: [{"value": "run foo", "if": ["42"], "env": [f"{env_var}=bar"]}]}} }, } } - project_config = ProjectConfig(isolation, {'envs': env_config}, PluginManager()) + project_config = ProjectConfig(isolation, {"envs": env_config}, PluginManager()) expected_envs = { - 'default': {'type': 'virtual'}, - 'foo.9000': {'type': 'virtual', option: ['run baz']}, - 'foo.42': {'type': 'virtual', option: ['run baz']}, - 'foo.bar': {'type': 'virtual', option: ['run baz']}, + "default": {"type": "virtual"}, + "foo.9000": {"type": "virtual", option: ["run baz"]}, + "foo.42": {"type": "virtual", option: ["run baz"]}, + "foo.bar": {"type": "virtual", option: ["run baz"]}, } - with EnvVars({env_var: 'baz'}): + with EnvVars({env_var: "baz"}): assert project_config.envs == expected_envs - assert project_config.matrices['foo'] == construct_matrix_data('foo', env_config) + assert project_config.matrices["foo"] == construct_matrix_data("foo", env_config) - @pytest.mark.parametrize('option', ARRAY_OPTIONS) + @pytest.mark.parametrize("option", ARRAY_OPTIONS) def test_overrides_matrix_array_table_conditional_with_env_var_missing(self, isolation, option): - env_var = 'OVERRIDES_ENV_FOO' + env_var = "OVERRIDES_ENV_FOO" env_config = { - 'foo': { - option: ['run baz'], - 'matrix': [{'version': ['9000', '42']}, {'feature': ['bar']}], - 'overrides': { - 'matrix': {'version': {option: [{'value': 'run foo', 'if': ['42'], 'env': [f'{env_var}=bar']}]}} + "foo": { + option: ["run baz"], + "matrix": [{"version": ["9000", "42"]}, {"feature": ["bar"]}], + "overrides": { + "matrix": {"version": {option: [{"value": "run foo", "if": ["42"], "env": [f"{env_var}=bar"]}]}} }, } } - project_config = ProjectConfig(isolation, {'envs': env_config}, PluginManager()) + project_config = ProjectConfig(isolation, {"envs": env_config}, PluginManager()) expected_envs = { - 'default': {'type': 'virtual'}, - 'foo.9000': {'type': 'virtual', option: ['run baz']}, - 'foo.42': {'type': 'virtual', option: ['run baz']}, - 'foo.bar': {'type': 'virtual', option: ['run baz']}, + "default": {"type": "virtual"}, + "foo.9000": {"type": "virtual", option: ["run baz"]}, + "foo.42": {"type": "virtual", option: ["run baz"]}, + "foo.bar": {"type": "virtual", option: ["run baz"]}, } assert project_config.envs == expected_envs - assert project_config.matrices['foo'] == construct_matrix_data('foo', env_config) + assert project_config.matrices["foo"] == construct_matrix_data("foo", env_config) def test_overrides_matrix_set_with_no_type_information(self, isolation): - env_var = 'OVERRIDES_ENV_FOO' + env_var = "OVERRIDES_ENV_FOO" env_config = { - 'foo': { - 'matrix': [{'version': ['9000', '42']}, {'feature': ['bar']}], - 'overrides': { - 'matrix': {'version': {'bar': {'value': ['baz'], 'if': ['42'], 'env': [f'{env_var}=bar']}}} + "foo": { + "matrix": [{"version": ["9000", "42"]}, {"feature": ["bar"]}], + "overrides": { + "matrix": {"version": {"bar": {"value": ["baz"], "if": ["42"], "env": [f"{env_var}=bar"]}}} }, } } - project_config = ProjectConfig(isolation, {'envs': env_config}, PluginManager()) + project_config = ProjectConfig(isolation, {"envs": env_config}, PluginManager()) expected_envs = { - 'default': {'type': 'virtual'}, - 'foo.9000': {'type': 'virtual'}, - 'foo.42': {'type': 'virtual', 'bar': ['baz']}, - 'foo.bar': {'type': 'virtual'}, + "default": {"type": "virtual"}, + "foo.9000": {"type": "virtual"}, + "foo.42": {"type": "virtual", "bar": ["baz"]}, + "foo.bar": {"type": "virtual"}, } - with EnvVars({env_var: 'bar'}): + with EnvVars({env_var: "bar"}): assert project_config.envs == expected_envs - assert project_config.matrices['foo'] == construct_matrix_data('foo', env_config) + assert project_config.matrices["foo"] == construct_matrix_data("foo", env_config) def test_overrides_matrix_set_with_no_type_information_not_table(self, isolation): project_config = ProjectConfig( isolation, { - 'envs': { - 'foo': { - 'matrix': [{'version': ['9000', '42']}, {'feature': ['bar']}], - 'overrides': {'matrix': {'version': {'bar': 9000}}}, + "envs": { + "foo": { + "matrix": [{"version": ["9000", "42"]}, {"feature": ["bar"]}], + "overrides": {"matrix": {"version": {"bar": 9000}}}, } } }, @@ -1687,686 +1687,686 @@ def test_overrides_matrix_set_with_no_type_information_not_table(self, isolation with pytest.raises( ValueError, match=( - 'Untyped option `tool.hatch.envs.foo.9000.overrides.matrix.version.bar` ' - 'must be defined as a table with a `value` key' + "Untyped option `tool.hatch.envs.foo.9000.overrides.matrix.version.bar` " + "must be defined as a table with a `value` key" ), ): project_config.finalize_env_overrides({}) - @pytest.mark.parametrize('option', ARRAY_OPTIONS) + @pytest.mark.parametrize("option", ARRAY_OPTIONS) def test_overrides_matrix_array_overwrite(self, isolation, option): env_config = { - 'foo': { - option: ['run baz'], - 'matrix': [{'version': ['9000']}, {'feature': ['bar']}], - 'overrides': {'matrix': {'version': {f'set-{option}': ['run foo', {'value': 'run bar'}]}}}, + "foo": { + option: ["run baz"], + "matrix": [{"version": ["9000"]}, {"feature": ["bar"]}], + "overrides": {"matrix": {"version": {f"set-{option}": ["run foo", {"value": "run bar"}]}}}, } } - project_config = ProjectConfig(isolation, {'envs': env_config}, PluginManager()) + project_config = ProjectConfig(isolation, {"envs": env_config}, PluginManager()) expected_envs = { - 'default': {'type': 'virtual'}, - 'foo.9000': {'type': 'virtual', option: ['run foo', 'run bar']}, - 'foo.bar': {'type': 'virtual', option: ['run baz']}, + "default": {"type": "virtual"}, + "foo.9000": {"type": "virtual", option: ["run foo", "run bar"]}, + "foo.bar": {"type": "virtual", option: ["run baz"]}, } assert project_config.envs == expected_envs - assert project_config.matrices['foo'] == construct_matrix_data('foo', env_config) + assert project_config.matrices["foo"] == construct_matrix_data("foo", env_config) - @pytest.mark.parametrize('option', STRING_OPTIONS) + @pytest.mark.parametrize("option", STRING_OPTIONS) def test_overrides_matrix_string_string_create(self, isolation, option): env_config = { - 'foo': { - 'matrix': [{'version': ['9000']}, {'feature': ['bar']}], - 'overrides': {'matrix': {'version': {option: 'baz'}}}, + "foo": { + "matrix": [{"version": ["9000"]}, {"feature": ["bar"]}], + "overrides": {"matrix": {"version": {option: "baz"}}}, } } - project_config = ProjectConfig(isolation, {'envs': env_config}, PluginManager()) + project_config = ProjectConfig(isolation, {"envs": env_config}, PluginManager()) expected_envs = { - 'default': {'type': 'virtual'}, - 'foo.9000': {'type': 'virtual', option: 'baz'}, - 'foo.bar': {'type': 'virtual'}, + "default": {"type": "virtual"}, + "foo.9000": {"type": "virtual", option: "baz"}, + "foo.bar": {"type": "virtual"}, } assert project_config.envs == expected_envs - assert project_config.matrices['foo'] == construct_matrix_data('foo', env_config) + assert project_config.matrices["foo"] == construct_matrix_data("foo", env_config) - @pytest.mark.parametrize('option', STRING_OPTIONS) + @pytest.mark.parametrize("option", STRING_OPTIONS) def test_overrides_matrix_string_string_overwrite(self, isolation, option): env_config = { - 'foo': { - option: 'test', - 'matrix': [{'version': ['9000']}, {'feature': ['bar']}], - 'overrides': {'matrix': {'version': {option: 'baz'}}}, + "foo": { + option: "test", + "matrix": [{"version": ["9000"]}, {"feature": ["bar"]}], + "overrides": {"matrix": {"version": {option: "baz"}}}, } } - project_config = ProjectConfig(isolation, {'envs': env_config}, PluginManager()) + project_config = ProjectConfig(isolation, {"envs": env_config}, PluginManager()) expected_envs = { - 'default': {'type': 'virtual'}, - 'foo.9000': {'type': 'virtual', option: 'baz'}, - 'foo.bar': {'type': 'virtual', option: 'test'}, + "default": {"type": "virtual"}, + "foo.9000": {"type": "virtual", option: "baz"}, + "foo.bar": {"type": "virtual", option: "test"}, } assert project_config.envs == expected_envs - assert project_config.matrices['foo'] == construct_matrix_data('foo', env_config) + assert project_config.matrices["foo"] == construct_matrix_data("foo", env_config) - @pytest.mark.parametrize('option', STRING_OPTIONS) + @pytest.mark.parametrize("option", STRING_OPTIONS) def test_overrides_matrix_string_table_create(self, isolation, option): env_config = { - 'foo': { - 'matrix': [{'version': ['9000']}, {'feature': ['bar']}], - 'overrides': {'matrix': {'version': {option: {'value': 'baz'}}}}, + "foo": { + "matrix": [{"version": ["9000"]}, {"feature": ["bar"]}], + "overrides": {"matrix": {"version": {option: {"value": "baz"}}}}, } } - project_config = ProjectConfig(isolation, {'envs': env_config}, PluginManager()) + project_config = ProjectConfig(isolation, {"envs": env_config}, PluginManager()) expected_envs = { - 'default': {'type': 'virtual'}, - 'foo.9000': {'type': 'virtual', option: 'baz'}, - 'foo.bar': {'type': 'virtual'}, + "default": {"type": "virtual"}, + "foo.9000": {"type": "virtual", option: "baz"}, + "foo.bar": {"type": "virtual"}, } assert project_config.envs == expected_envs - assert project_config.matrices['foo'] == construct_matrix_data('foo', env_config) + assert project_config.matrices["foo"] == construct_matrix_data("foo", env_config) - @pytest.mark.parametrize('option', STRING_OPTIONS) + @pytest.mark.parametrize("option", STRING_OPTIONS) def test_overrides_matrix_string_table_override(self, isolation, option): env_config = { - 'foo': { - option: 'test', - 'matrix': [{'version': ['9000']}, {'feature': ['bar']}], - 'overrides': {'matrix': {'version': {option: {'value': 'baz'}}}}, + "foo": { + option: "test", + "matrix": [{"version": ["9000"]}, {"feature": ["bar"]}], + "overrides": {"matrix": {"version": {option: {"value": "baz"}}}}, } } - project_config = ProjectConfig(isolation, {'envs': env_config}, PluginManager()) + project_config = ProjectConfig(isolation, {"envs": env_config}, PluginManager()) expected_envs = { - 'default': {'type': 'virtual'}, - 'foo.9000': {'type': 'virtual', option: 'baz'}, - 'foo.bar': {'type': 'virtual', option: 'test'}, + "default": {"type": "virtual"}, + "foo.9000": {"type": "virtual", option: "baz"}, + "foo.bar": {"type": "virtual", option: "test"}, } assert project_config.envs == expected_envs - assert project_config.matrices['foo'] == construct_matrix_data('foo', env_config) + assert project_config.matrices["foo"] == construct_matrix_data("foo", env_config) - @pytest.mark.parametrize('option', STRING_OPTIONS) + @pytest.mark.parametrize("option", STRING_OPTIONS) def test_overrides_matrix_string_table_conditional(self, isolation, option): env_config = { - 'foo': { - option: 'test', - 'matrix': [{'version': ['9000', '42']}, {'feature': ['bar']}], - 'overrides': {'matrix': {'version': {option: {'value': 'baz', 'if': ['42']}}}}, + "foo": { + option: "test", + "matrix": [{"version": ["9000", "42"]}, {"feature": ["bar"]}], + "overrides": {"matrix": {"version": {option: {"value": "baz", "if": ["42"]}}}}, } } - project_config = ProjectConfig(isolation, {'envs': env_config}, PluginManager()) + project_config = ProjectConfig(isolation, {"envs": env_config}, PluginManager()) expected_envs = { - 'default': {'type': 'virtual'}, - 'foo.9000': {'type': 'virtual', option: 'test'}, - 'foo.42': {'type': 'virtual', option: 'baz'}, - 'foo.bar': {'type': 'virtual', option: 'test'}, + "default": {"type": "virtual"}, + "foo.9000": {"type": "virtual", option: "test"}, + "foo.42": {"type": "virtual", option: "baz"}, + "foo.bar": {"type": "virtual", option: "test"}, } assert project_config.envs == expected_envs - assert project_config.matrices['foo'] == construct_matrix_data('foo', env_config) + assert project_config.matrices["foo"] == construct_matrix_data("foo", env_config) - @pytest.mark.parametrize('option', STRING_OPTIONS) + @pytest.mark.parametrize("option", STRING_OPTIONS) def test_overrides_matrix_string_array_table_create(self, isolation, option): env_config = { - 'foo': { - 'matrix': [{'version': ['9000']}, {'feature': ['bar']}], - 'overrides': {'matrix': {'version': {option: [{'value': 'baz'}]}}}, + "foo": { + "matrix": [{"version": ["9000"]}, {"feature": ["bar"]}], + "overrides": {"matrix": {"version": {option: [{"value": "baz"}]}}}, } } - project_config = ProjectConfig(isolation, {'envs': env_config}, PluginManager()) + project_config = ProjectConfig(isolation, {"envs": env_config}, PluginManager()) expected_envs = { - 'default': {'type': 'virtual'}, - 'foo.9000': {'type': 'virtual', option: 'baz'}, - 'foo.bar': {'type': 'virtual'}, + "default": {"type": "virtual"}, + "foo.9000": {"type": "virtual", option: "baz"}, + "foo.bar": {"type": "virtual"}, } assert project_config.envs == expected_envs - assert project_config.matrices['foo'] == construct_matrix_data('foo', env_config) + assert project_config.matrices["foo"] == construct_matrix_data("foo", env_config) - @pytest.mark.parametrize('option', STRING_OPTIONS) + @pytest.mark.parametrize("option", STRING_OPTIONS) def test_overrides_matrix_string_array_table_override(self, isolation, option): env_config = { - 'foo': { - option: 'test', - 'matrix': [{'version': ['9000']}, {'feature': ['bar']}], - 'overrides': {'matrix': {'version': {option: [{'value': 'baz'}]}}}, + "foo": { + option: "test", + "matrix": [{"version": ["9000"]}, {"feature": ["bar"]}], + "overrides": {"matrix": {"version": {option: [{"value": "baz"}]}}}, } } - project_config = ProjectConfig(isolation, {'envs': env_config}, PluginManager()) + project_config = ProjectConfig(isolation, {"envs": env_config}, PluginManager()) expected_envs = { - 'default': {'type': 'virtual'}, - 'foo.9000': {'type': 'virtual', option: 'baz'}, - 'foo.bar': {'type': 'virtual', option: 'test'}, + "default": {"type": "virtual"}, + "foo.9000": {"type": "virtual", option: "baz"}, + "foo.bar": {"type": "virtual", option: "test"}, } assert project_config.envs == expected_envs - assert project_config.matrices['foo'] == construct_matrix_data('foo', env_config) + assert project_config.matrices["foo"] == construct_matrix_data("foo", env_config) - @pytest.mark.parametrize('option', STRING_OPTIONS) + @pytest.mark.parametrize("option", STRING_OPTIONS) def test_overrides_matrix_string_array_table_conditional(self, isolation, option): env_config = { - 'foo': { - option: 'test', - 'matrix': [{'version': ['9000', '42']}, {'feature': ['bar']}], - 'overrides': {'matrix': {'version': {option: [{'value': 'baz', 'if': ['42']}]}}}, + "foo": { + option: "test", + "matrix": [{"version": ["9000", "42"]}, {"feature": ["bar"]}], + "overrides": {"matrix": {"version": {option: [{"value": "baz", "if": ["42"]}]}}}, } } - project_config = ProjectConfig(isolation, {'envs': env_config}, PluginManager()) + project_config = ProjectConfig(isolation, {"envs": env_config}, PluginManager()) expected_envs = { - 'default': {'type': 'virtual'}, - 'foo.9000': {'type': 'virtual', option: 'test'}, - 'foo.42': {'type': 'virtual', option: 'baz'}, - 'foo.bar': {'type': 'virtual', option: 'test'}, + "default": {"type": "virtual"}, + "foo.9000": {"type": "virtual", option: "test"}, + "foo.42": {"type": "virtual", option: "baz"}, + "foo.bar": {"type": "virtual", option: "test"}, } assert project_config.envs == expected_envs - assert project_config.matrices['foo'] == construct_matrix_data('foo', env_config) + assert project_config.matrices["foo"] == construct_matrix_data("foo", env_config) - @pytest.mark.parametrize('option', STRING_OPTIONS) + @pytest.mark.parametrize("option", STRING_OPTIONS) def test_overrides_matrix_string_array_table_conditional_eager_string(self, isolation, option): env_config = { - 'foo': { - option: 'test', - 'matrix': [{'version': ['9000', '42']}, {'feature': ['bar']}], - 'overrides': {'matrix': {'version': {option: ['baz', {'value': 'foo', 'if': ['42']}]}}}, + "foo": { + option: "test", + "matrix": [{"version": ["9000", "42"]}, {"feature": ["bar"]}], + "overrides": {"matrix": {"version": {option: ["baz", {"value": "foo", "if": ["42"]}]}}}, } } - project_config = ProjectConfig(isolation, {'envs': env_config}, PluginManager()) + project_config = ProjectConfig(isolation, {"envs": env_config}, PluginManager()) expected_envs = { - 'default': {'type': 'virtual'}, - 'foo.9000': {'type': 'virtual', option: 'baz'}, - 'foo.42': {'type': 'virtual', option: 'baz'}, - 'foo.bar': {'type': 'virtual', option: 'test'}, + "default": {"type": "virtual"}, + "foo.9000": {"type": "virtual", option: "baz"}, + "foo.42": {"type": "virtual", option: "baz"}, + "foo.bar": {"type": "virtual", option: "test"}, } assert project_config.envs == expected_envs - assert project_config.matrices['foo'] == construct_matrix_data('foo', env_config) + assert project_config.matrices["foo"] == construct_matrix_data("foo", env_config) - @pytest.mark.parametrize('option', STRING_OPTIONS) + @pytest.mark.parametrize("option", STRING_OPTIONS) def test_overrides_matrix_string_array_table_conditional_eager_table(self, isolation, option): env_config = { - 'foo': { - option: 'test', - 'matrix': [{'version': ['9000', '42']}, {'feature': ['bar']}], - 'overrides': {'matrix': {'version': {option: [{'value': 'baz', 'if': ['42']}, 'foo']}}}, + "foo": { + option: "test", + "matrix": [{"version": ["9000", "42"]}, {"feature": ["bar"]}], + "overrides": {"matrix": {"version": {option: [{"value": "baz", "if": ["42"]}, "foo"]}}}, } } - project_config = ProjectConfig(isolation, {'envs': env_config}, PluginManager()) + project_config = ProjectConfig(isolation, {"envs": env_config}, PluginManager()) expected_envs = { - 'default': {'type': 'virtual'}, - 'foo.9000': {'type': 'virtual', option: 'foo'}, - 'foo.42': {'type': 'virtual', option: 'baz'}, - 'foo.bar': {'type': 'virtual', option: 'test'}, + "default": {"type": "virtual"}, + "foo.9000": {"type": "virtual", option: "foo"}, + "foo.42": {"type": "virtual", option: "baz"}, + "foo.bar": {"type": "virtual", option: "test"}, } assert project_config.envs == expected_envs - assert project_config.matrices['foo'] == construct_matrix_data('foo', env_config) + assert project_config.matrices["foo"] == construct_matrix_data("foo", env_config) - @pytest.mark.parametrize('option', BOOLEAN_OPTIONS) + @pytest.mark.parametrize("option", BOOLEAN_OPTIONS) def test_overrides_matrix_boolean_boolean_create(self, isolation, option): env_config = { - 'foo': { - 'matrix': [{'version': ['9000']}, {'feature': ['bar']}], - 'overrides': {'matrix': {'version': {option: True}}}, + "foo": { + "matrix": [{"version": ["9000"]}, {"feature": ["bar"]}], + "overrides": {"matrix": {"version": {option: True}}}, } } - project_config = ProjectConfig(isolation, {'envs': env_config}, PluginManager()) + project_config = ProjectConfig(isolation, {"envs": env_config}, PluginManager()) expected_envs = { - 'default': {'type': 'virtual'}, - 'foo.9000': {'type': 'virtual', option: True}, - 'foo.bar': {'type': 'virtual'}, + "default": {"type": "virtual"}, + "foo.9000": {"type": "virtual", option: True}, + "foo.bar": {"type": "virtual"}, } assert project_config.envs == expected_envs - assert project_config.matrices['foo'] == construct_matrix_data('foo', env_config) + assert project_config.matrices["foo"] == construct_matrix_data("foo", env_config) - @pytest.mark.parametrize('option', BOOLEAN_OPTIONS) + @pytest.mark.parametrize("option", BOOLEAN_OPTIONS) def test_overrides_matrix_boolean_boolean_overwrite(self, isolation, option): env_config = { - 'foo': { + "foo": { option: False, - 'matrix': [{'version': ['9000']}, {'feature': ['bar']}], - 'overrides': {'matrix': {'version': {option: True}}}, + "matrix": [{"version": ["9000"]}, {"feature": ["bar"]}], + "overrides": {"matrix": {"version": {option: True}}}, } } - project_config = ProjectConfig(isolation, {'envs': env_config}, PluginManager()) + project_config = ProjectConfig(isolation, {"envs": env_config}, PluginManager()) expected_envs = { - 'default': {'type': 'virtual'}, - 'foo.9000': {'type': 'virtual', option: True}, - 'foo.bar': {'type': 'virtual', option: False}, + "default": {"type": "virtual"}, + "foo.9000": {"type": "virtual", option: True}, + "foo.bar": {"type": "virtual", option: False}, } assert project_config.envs == expected_envs - assert project_config.matrices['foo'] == construct_matrix_data('foo', env_config) + assert project_config.matrices["foo"] == construct_matrix_data("foo", env_config) - @pytest.mark.parametrize('option', BOOLEAN_OPTIONS) + @pytest.mark.parametrize("option", BOOLEAN_OPTIONS) def test_overrides_matrix_boolean_table_create(self, isolation, option): env_config = { - 'foo': { - 'matrix': [{'version': ['9000']}, {'feature': ['bar']}], - 'overrides': {'matrix': {'version': {option: {'value': True}}}}, + "foo": { + "matrix": [{"version": ["9000"]}, {"feature": ["bar"]}], + "overrides": {"matrix": {"version": {option: {"value": True}}}}, } } - project_config = ProjectConfig(isolation, {'envs': env_config}, PluginManager()) + project_config = ProjectConfig(isolation, {"envs": env_config}, PluginManager()) expected_envs = { - 'default': {'type': 'virtual'}, - 'foo.9000': {'type': 'virtual', option: True}, - 'foo.bar': {'type': 'virtual'}, + "default": {"type": "virtual"}, + "foo.9000": {"type": "virtual", option: True}, + "foo.bar": {"type": "virtual"}, } assert project_config.envs == expected_envs - assert project_config.matrices['foo'] == construct_matrix_data('foo', env_config) + assert project_config.matrices["foo"] == construct_matrix_data("foo", env_config) - @pytest.mark.parametrize('option', BOOLEAN_OPTIONS) + @pytest.mark.parametrize("option", BOOLEAN_OPTIONS) def test_overrides_matrix_boolean_table_override(self, isolation, option): env_config = { - 'foo': { + "foo": { option: False, - 'matrix': [{'version': ['9000']}, {'feature': ['bar']}], - 'overrides': {'matrix': {'version': {option: {'value': True}}}}, + "matrix": [{"version": ["9000"]}, {"feature": ["bar"]}], + "overrides": {"matrix": {"version": {option: {"value": True}}}}, } } - project_config = ProjectConfig(isolation, {'envs': env_config}, PluginManager()) + project_config = ProjectConfig(isolation, {"envs": env_config}, PluginManager()) expected_envs = { - 'default': {'type': 'virtual'}, - 'foo.9000': {'type': 'virtual', option: True}, - 'foo.bar': {'type': 'virtual', option: False}, + "default": {"type": "virtual"}, + "foo.9000": {"type": "virtual", option: True}, + "foo.bar": {"type": "virtual", option: False}, } assert project_config.envs == expected_envs - assert project_config.matrices['foo'] == construct_matrix_data('foo', env_config) + assert project_config.matrices["foo"] == construct_matrix_data("foo", env_config) - @pytest.mark.parametrize('option', BOOLEAN_OPTIONS) + @pytest.mark.parametrize("option", BOOLEAN_OPTIONS) def test_overrides_matrix_boolean_table_conditional(self, isolation, option): env_config = { - 'foo': { + "foo": { option: False, - 'matrix': [{'version': ['9000', '42']}, {'feature': ['bar']}], - 'overrides': {'matrix': {'version': {option: {'value': True, 'if': ['42']}}}}, + "matrix": [{"version": ["9000", "42"]}, {"feature": ["bar"]}], + "overrides": {"matrix": {"version": {option: {"value": True, "if": ["42"]}}}}, } } - project_config = ProjectConfig(isolation, {'envs': env_config}, PluginManager()) + project_config = ProjectConfig(isolation, {"envs": env_config}, PluginManager()) expected_envs = { - 'default': {'type': 'virtual'}, - 'foo.9000': {'type': 'virtual', option: False}, - 'foo.42': {'type': 'virtual', option: True}, - 'foo.bar': {'type': 'virtual', option: False}, + "default": {"type": "virtual"}, + "foo.9000": {"type": "virtual", option: False}, + "foo.42": {"type": "virtual", option: True}, + "foo.bar": {"type": "virtual", option: False}, } assert project_config.envs == expected_envs - assert project_config.matrices['foo'] == construct_matrix_data('foo', env_config) + assert project_config.matrices["foo"] == construct_matrix_data("foo", env_config) - @pytest.mark.parametrize('option', BOOLEAN_OPTIONS) + @pytest.mark.parametrize("option", BOOLEAN_OPTIONS) def test_overrides_matrix_boolean_array_table_create(self, isolation, option): env_config = { - 'foo': { - 'matrix': [{'version': ['9000']}, {'feature': ['bar']}], - 'overrides': {'matrix': {'version': {option: [{'value': True}]}}}, + "foo": { + "matrix": [{"version": ["9000"]}, {"feature": ["bar"]}], + "overrides": {"matrix": {"version": {option: [{"value": True}]}}}, } } - project_config = ProjectConfig(isolation, {'envs': env_config}, PluginManager()) + project_config = ProjectConfig(isolation, {"envs": env_config}, PluginManager()) expected_envs = { - 'default': {'type': 'virtual'}, - 'foo.9000': {'type': 'virtual', option: True}, - 'foo.bar': {'type': 'virtual'}, + "default": {"type": "virtual"}, + "foo.9000": {"type": "virtual", option: True}, + "foo.bar": {"type": "virtual"}, } assert project_config.envs == expected_envs - assert project_config.matrices['foo'] == construct_matrix_data('foo', env_config) + assert project_config.matrices["foo"] == construct_matrix_data("foo", env_config) - @pytest.mark.parametrize('option', BOOLEAN_OPTIONS) + @pytest.mark.parametrize("option", BOOLEAN_OPTIONS) def test_overrides_matrix_boolean_array_table_override(self, isolation, option): env_config = { - 'foo': { + "foo": { option: False, - 'matrix': [{'version': ['9000']}, {'feature': ['bar']}], - 'overrides': {'matrix': {'version': {option: [{'value': True}]}}}, + "matrix": [{"version": ["9000"]}, {"feature": ["bar"]}], + "overrides": {"matrix": {"version": {option: [{"value": True}]}}}, } } - project_config = ProjectConfig(isolation, {'envs': env_config}, PluginManager()) + project_config = ProjectConfig(isolation, {"envs": env_config}, PluginManager()) expected_envs = { - 'default': {'type': 'virtual'}, - 'foo.9000': {'type': 'virtual', option: True}, - 'foo.bar': {'type': 'virtual', option: False}, + "default": {"type": "virtual"}, + "foo.9000": {"type": "virtual", option: True}, + "foo.bar": {"type": "virtual", option: False}, } assert project_config.envs == expected_envs - assert project_config.matrices['foo'] == construct_matrix_data('foo', env_config) + assert project_config.matrices["foo"] == construct_matrix_data("foo", env_config) - @pytest.mark.parametrize('option', BOOLEAN_OPTIONS) + @pytest.mark.parametrize("option", BOOLEAN_OPTIONS) def test_overrides_matrix_boolean_array_table_conditional(self, isolation, option): env_config = { - 'foo': { + "foo": { option: False, - 'matrix': [{'version': ['9000', '42']}, {'feature': ['bar']}], - 'overrides': {'matrix': {'version': {option: [{'value': True, 'if': ['42']}]}}}, + "matrix": [{"version": ["9000", "42"]}, {"feature": ["bar"]}], + "overrides": {"matrix": {"version": {option: [{"value": True, "if": ["42"]}]}}}, } } - project_config = ProjectConfig(isolation, {'envs': env_config}, PluginManager()) + project_config = ProjectConfig(isolation, {"envs": env_config}, PluginManager()) expected_envs = { - 'default': {'type': 'virtual'}, - 'foo.9000': {'type': 'virtual', option: False}, - 'foo.42': {'type': 'virtual', option: True}, - 'foo.bar': {'type': 'virtual', option: False}, + "default": {"type": "virtual"}, + "foo.9000": {"type": "virtual", option: False}, + "foo.42": {"type": "virtual", option: True}, + "foo.bar": {"type": "virtual", option: False}, } assert project_config.envs == expected_envs - assert project_config.matrices['foo'] == construct_matrix_data('foo', env_config) + assert project_config.matrices["foo"] == construct_matrix_data("foo", env_config) - @pytest.mark.parametrize('option', BOOLEAN_OPTIONS) + @pytest.mark.parametrize("option", BOOLEAN_OPTIONS) def test_overrides_matrix_boolean_array_table_conditional_eager_boolean(self, isolation, option): env_config = { - 'foo': { + "foo": { option: False, - 'matrix': [{'version': ['9000', '42']}, {'feature': ['bar']}], - 'overrides': {'matrix': {'version': {option: [True, {'value': False, 'if': ['42']}]}}}, + "matrix": [{"version": ["9000", "42"]}, {"feature": ["bar"]}], + "overrides": {"matrix": {"version": {option: [True, {"value": False, "if": ["42"]}]}}}, } } - project_config = ProjectConfig(isolation, {'envs': env_config}, PluginManager()) + project_config = ProjectConfig(isolation, {"envs": env_config}, PluginManager()) expected_envs = { - 'default': {'type': 'virtual'}, - 'foo.9000': {'type': 'virtual', option: True}, - 'foo.42': {'type': 'virtual', option: True}, - 'foo.bar': {'type': 'virtual', option: False}, + "default": {"type": "virtual"}, + "foo.9000": {"type": "virtual", option: True}, + "foo.42": {"type": "virtual", option: True}, + "foo.bar": {"type": "virtual", option: False}, } assert project_config.envs == expected_envs - assert project_config.matrices['foo'] == construct_matrix_data('foo', env_config) + assert project_config.matrices["foo"] == construct_matrix_data("foo", env_config) - @pytest.mark.parametrize('option', BOOLEAN_OPTIONS) + @pytest.mark.parametrize("option", BOOLEAN_OPTIONS) def test_overrides_matrix_boolean_array_table_conditional_eager_table(self, isolation, option): env_config = { - 'foo': { + "foo": { option: False, - 'matrix': [{'version': ['9000', '42']}, {'feature': ['bar']}], - 'overrides': {'matrix': {'version': {option: [{'value': True, 'if': ['42']}, False]}}}, + "matrix": [{"version": ["9000", "42"]}, {"feature": ["bar"]}], + "overrides": {"matrix": {"version": {option: [{"value": True, "if": ["42"]}, False]}}}, } } - project_config = ProjectConfig(isolation, {'envs': env_config}, PluginManager()) + project_config = ProjectConfig(isolation, {"envs": env_config}, PluginManager()) expected_envs = { - 'default': {'type': 'virtual'}, - 'foo.9000': {'type': 'virtual', option: False}, - 'foo.42': {'type': 'virtual', option: True}, - 'foo.bar': {'type': 'virtual', option: False}, + "default": {"type": "virtual"}, + "foo.9000": {"type": "virtual", option: False}, + "foo.42": {"type": "virtual", option: True}, + "foo.bar": {"type": "virtual", option: False}, } assert project_config.envs == expected_envs - assert project_config.matrices['foo'] == construct_matrix_data('foo', env_config) + assert project_config.matrices["foo"] == construct_matrix_data("foo", env_config) # We assert type coverage using matrix variable overrides, for the others just test one type def test_overrides_platform_boolean_boolean_create(self, isolation, current_platform): env_config = { - 'foo': { - 'overrides': {'platform': {'bar': {'dependencies': ['baz']}, current_platform: {'skip-install': True}}} + "foo": { + "overrides": {"platform": {"bar": {"dependencies": ["baz"]}, current_platform: {"skip-install": True}}} } } - project_config = ProjectConfig(isolation, {'envs': env_config}, PluginManager()) + project_config = ProjectConfig(isolation, {"envs": env_config}, PluginManager()) expected_envs = { - 'default': {'type': 'virtual'}, - 'foo': {'type': 'virtual', 'skip-install': True}, + "default": {"type": "virtual"}, + "foo": {"type": "virtual", "skip-install": True}, } assert project_config.envs == expected_envs def test_overrides_platform_boolean_boolean_overwrite(self, isolation, current_platform): env_config = { - 'foo': { - 'skip-install': True, - 'overrides': { - 'platform': {'bar': {'dependencies': ['baz']}, current_platform: {'skip-install': False}} + "foo": { + "skip-install": True, + "overrides": { + "platform": {"bar": {"dependencies": ["baz"]}, current_platform: {"skip-install": False}} }, } } - project_config = ProjectConfig(isolation, {'envs': env_config}, PluginManager()) + project_config = ProjectConfig(isolation, {"envs": env_config}, PluginManager()) expected_envs = { - 'default': {'type': 'virtual'}, - 'foo': {'type': 'virtual', 'skip-install': False}, + "default": {"type": "virtual"}, + "foo": {"type": "virtual", "skip-install": False}, } assert project_config.envs == expected_envs def test_overrides_platform_boolean_table_create(self, isolation, current_platform): env_config = { - 'foo': { - 'overrides': { - 'platform': { - 'bar': {'dependencies': ['baz']}, - current_platform: {'skip-install': [{'value': True}]}, + "foo": { + "overrides": { + "platform": { + "bar": {"dependencies": ["baz"]}, + current_platform: {"skip-install": [{"value": True}]}, } } } } - project_config = ProjectConfig(isolation, {'envs': env_config}, PluginManager()) + project_config = ProjectConfig(isolation, {"envs": env_config}, PluginManager()) expected_envs = { - 'default': {'type': 'virtual'}, - 'foo': {'type': 'virtual', 'skip-install': True}, + "default": {"type": "virtual"}, + "foo": {"type": "virtual", "skip-install": True}, } assert project_config.envs == expected_envs def test_overrides_platform_boolean_table_overwrite(self, isolation, current_platform): env_config = { - 'foo': { - 'skip-install': True, - 'overrides': { - 'platform': { - 'bar': {'dependencies': ['baz']}, - current_platform: {'skip-install': [{'value': False}]}, + "foo": { + "skip-install": True, + "overrides": { + "platform": { + "bar": {"dependencies": ["baz"]}, + current_platform: {"skip-install": [{"value": False}]}, } }, } } - project_config = ProjectConfig(isolation, {'envs': env_config}, PluginManager()) + project_config = ProjectConfig(isolation, {"envs": env_config}, PluginManager()) expected_envs = { - 'default': {'type': 'virtual'}, - 'foo': {'type': 'virtual', 'skip-install': False}, + "default": {"type": "virtual"}, + "foo": {"type": "virtual", "skip-install": False}, } assert project_config.envs == expected_envs def test_overrides_env_boolean_boolean_create(self, isolation): - env_var_exists = 'OVERRIDES_ENV_FOO' - env_var_missing = 'OVERRIDES_ENV_BAR' + env_var_exists = "OVERRIDES_ENV_FOO" + env_var_missing = "OVERRIDES_ENV_BAR" env_config = { - 'foo': { - 'overrides': { - 'env': {env_var_missing: {'dependencies': ['baz']}, env_var_exists: {'skip-install': True}} + "foo": { + "overrides": { + "env": {env_var_missing: {"dependencies": ["baz"]}, env_var_exists: {"skip-install": True}} } } } - project_config = ProjectConfig(isolation, {'envs': env_config}, PluginManager()) + project_config = ProjectConfig(isolation, {"envs": env_config}, PluginManager()) expected_envs = { - 'default': {'type': 'virtual'}, - 'foo': {'type': 'virtual', 'skip-install': True}, + "default": {"type": "virtual"}, + "foo": {"type": "virtual", "skip-install": True}, } - with EnvVars({env_var_exists: 'any'}): + with EnvVars({env_var_exists: "any"}): assert project_config.envs == expected_envs def test_overrides_env_boolean_boolean_overwrite(self, isolation): - env_var_exists = 'OVERRIDES_ENV_FOO' - env_var_missing = 'OVERRIDES_ENV_BAR' + env_var_exists = "OVERRIDES_ENV_FOO" + env_var_missing = "OVERRIDES_ENV_BAR" env_config = { - 'foo': { - 'skip-install': True, - 'overrides': { - 'env': {env_var_missing: {'dependencies': ['baz']}, env_var_exists: {'skip-install': False}} + "foo": { + "skip-install": True, + "overrides": { + "env": {env_var_missing: {"dependencies": ["baz"]}, env_var_exists: {"skip-install": False}} }, } } - project_config = ProjectConfig(isolation, {'envs': env_config}, PluginManager()) + project_config = ProjectConfig(isolation, {"envs": env_config}, PluginManager()) expected_envs = { - 'default': {'type': 'virtual'}, - 'foo': {'type': 'virtual', 'skip-install': False}, + "default": {"type": "virtual"}, + "foo": {"type": "virtual", "skip-install": False}, } - with EnvVars({env_var_exists: 'any'}): + with EnvVars({env_var_exists: "any"}): assert project_config.envs == expected_envs def test_overrides_env_boolean_table_create(self, isolation): - env_var_exists = 'OVERRIDES_ENV_FOO' - env_var_missing = 'OVERRIDES_ENV_BAR' - env_config = { - 'foo': { - 'overrides': { - 'env': { - env_var_missing: {'dependencies': ['baz']}, - env_var_exists: {'skip-install': [{'value': True}]}, + env_var_exists = "OVERRIDES_ENV_FOO" + env_var_missing = "OVERRIDES_ENV_BAR" + env_config = { + "foo": { + "overrides": { + "env": { + env_var_missing: {"dependencies": ["baz"]}, + env_var_exists: {"skip-install": [{"value": True}]}, } } } } - project_config = ProjectConfig(isolation, {'envs': env_config}, PluginManager()) + project_config = ProjectConfig(isolation, {"envs": env_config}, PluginManager()) expected_envs = { - 'default': {'type': 'virtual'}, - 'foo': {'type': 'virtual', 'skip-install': True}, + "default": {"type": "virtual"}, + "foo": {"type": "virtual", "skip-install": True}, } - with EnvVars({env_var_exists: 'any'}): + with EnvVars({env_var_exists: "any"}): assert project_config.envs == expected_envs def test_overrides_env_boolean_table_overwrite(self, isolation): - env_var_exists = 'OVERRIDES_ENV_FOO' - env_var_missing = 'OVERRIDES_ENV_BAR' - env_config = { - 'foo': { - 'skip-install': True, - 'overrides': { - 'env': { - env_var_missing: {'dependencies': ['baz']}, - env_var_exists: {'skip-install': [{'value': False}]}, + env_var_exists = "OVERRIDES_ENV_FOO" + env_var_missing = "OVERRIDES_ENV_BAR" + env_config = { + "foo": { + "skip-install": True, + "overrides": { + "env": { + env_var_missing: {"dependencies": ["baz"]}, + env_var_exists: {"skip-install": [{"value": False}]}, } }, } } - project_config = ProjectConfig(isolation, {'envs': env_config}, PluginManager()) + project_config = ProjectConfig(isolation, {"envs": env_config}, PluginManager()) expected_envs = { - 'default': {'type': 'virtual'}, - 'foo': {'type': 'virtual', 'skip-install': False}, + "default": {"type": "virtual"}, + "foo": {"type": "virtual", "skip-install": False}, } - with EnvVars({env_var_exists: 'any'}): + with EnvVars({env_var_exists: "any"}): assert project_config.envs == expected_envs def test_overrides_env_boolean_conditional(self, isolation): - env_var_exists = 'OVERRIDES_ENV_FOO' - env_var_missing = 'OVERRIDES_ENV_BAR' - env_config = { - 'foo': { - 'overrides': { - 'env': { - env_var_missing: {'dependencies': ['baz']}, - env_var_exists: {'skip-install': [{'value': True, 'if': ['foo']}]}, + env_var_exists = "OVERRIDES_ENV_FOO" + env_var_missing = "OVERRIDES_ENV_BAR" + env_config = { + "foo": { + "overrides": { + "env": { + env_var_missing: {"dependencies": ["baz"]}, + env_var_exists: {"skip-install": [{"value": True, "if": ["foo"]}]}, } } } } - project_config = ProjectConfig(isolation, {'envs': env_config}, PluginManager()) + project_config = ProjectConfig(isolation, {"envs": env_config}, PluginManager()) expected_envs = { - 'default': {'type': 'virtual'}, - 'foo': {'type': 'virtual', 'skip-install': True}, + "default": {"type": "virtual"}, + "foo": {"type": "virtual", "skip-install": True}, } - with EnvVars({env_var_exists: 'foo'}): + with EnvVars({env_var_exists: "foo"}): assert project_config.envs == expected_envs def test_overrides_name_boolean_boolean_create(self, isolation): env_config = { - 'foo': { - 'matrix': [{'version': ['9000']}, {'feature': ['bar']}], - 'overrides': {'name': {'bar$': {'skip-install': True}}}, + "foo": { + "matrix": [{"version": ["9000"]}, {"feature": ["bar"]}], + "overrides": {"name": {"bar$": {"skip-install": True}}}, } } - project_config = ProjectConfig(isolation, {'envs': env_config}, PluginManager()) + project_config = ProjectConfig(isolation, {"envs": env_config}, PluginManager()) expected_envs = { - 'default': {'type': 'virtual'}, - 'foo.9000': {'type': 'virtual'}, - 'foo.bar': {'type': 'virtual', 'skip-install': True}, + "default": {"type": "virtual"}, + "foo.9000": {"type": "virtual"}, + "foo.bar": {"type": "virtual", "skip-install": True}, } assert project_config.envs == expected_envs def test_overrides_name_boolean_boolean_overwrite(self, isolation): env_config = { - 'foo': { - 'skip-install': True, - 'matrix': [{'version': ['9000']}, {'feature': ['bar']}], - 'overrides': {'name': {'bar$': {'skip-install': False}}}, + "foo": { + "skip-install": True, + "matrix": [{"version": ["9000"]}, {"feature": ["bar"]}], + "overrides": {"name": {"bar$": {"skip-install": False}}}, } } - project_config = ProjectConfig(isolation, {'envs': env_config}, PluginManager()) + project_config = ProjectConfig(isolation, {"envs": env_config}, PluginManager()) expected_envs = { - 'default': {'type': 'virtual'}, - 'foo.9000': {'type': 'virtual', 'skip-install': True}, - 'foo.bar': {'type': 'virtual', 'skip-install': False}, + "default": {"type": "virtual"}, + "foo.9000": {"type": "virtual", "skip-install": True}, + "foo.bar": {"type": "virtual", "skip-install": False}, } assert project_config.envs == expected_envs def test_overrides_name_boolean_table_create(self, isolation): env_config = { - 'foo': { - 'matrix': [{'version': ['9000']}, {'feature': ['bar']}], - 'overrides': {'name': {'bar$': {'skip-install': [{'value': True}]}}}, + "foo": { + "matrix": [{"version": ["9000"]}, {"feature": ["bar"]}], + "overrides": {"name": {"bar$": {"skip-install": [{"value": True}]}}}, } } - project_config = ProjectConfig(isolation, {'envs': env_config}, PluginManager()) + project_config = ProjectConfig(isolation, {"envs": env_config}, PluginManager()) expected_envs = { - 'default': {'type': 'virtual'}, - 'foo.9000': {'type': 'virtual'}, - 'foo.bar': {'type': 'virtual', 'skip-install': True}, + "default": {"type": "virtual"}, + "foo.9000": {"type": "virtual"}, + "foo.bar": {"type": "virtual", "skip-install": True}, } assert project_config.envs == expected_envs def test_overrides_name_boolean_table_overwrite(self, isolation): env_config = { - 'foo': { - 'skip-install': True, - 'matrix': [{'version': ['9000']}, {'feature': ['bar']}], - 'overrides': {'name': {'bar$': {'skip-install': [{'value': False}]}}}, + "foo": { + "skip-install": True, + "matrix": [{"version": ["9000"]}, {"feature": ["bar"]}], + "overrides": {"name": {"bar$": {"skip-install": [{"value": False}]}}}, } } - project_config = ProjectConfig(isolation, {'envs': env_config}, PluginManager()) + project_config = ProjectConfig(isolation, {"envs": env_config}, PluginManager()) expected_envs = { - 'default': {'type': 'virtual'}, - 'foo.9000': {'type': 'virtual', 'skip-install': True}, - 'foo.bar': {'type': 'virtual', 'skip-install': False}, + "default": {"type": "virtual"}, + "foo.9000": {"type": "virtual", "skip-install": True}, + "foo.bar": {"type": "virtual", "skip-install": False}, } assert project_config.envs == expected_envs @@ -2374,130 +2374,130 @@ def test_overrides_name_boolean_table_overwrite(self, isolation): # Tests for source precedence def test_overrides_name_precedence_over_matrix(self, isolation): env_config = { - 'foo': { - 'skip-install': False, - 'matrix': [{'version': ['9000', '42']}, {'feature': ['bar']}], - 'overrides': { - 'name': {'42$': {'skip-install': False}}, - 'matrix': {'version': {'skip-install': [{'value': True, 'if': ['42']}]}}, + "foo": { + "skip-install": False, + "matrix": [{"version": ["9000", "42"]}, {"feature": ["bar"]}], + "overrides": { + "name": {"42$": {"skip-install": False}}, + "matrix": {"version": {"skip-install": [{"value": True, "if": ["42"]}]}}, }, } } - project_config = ProjectConfig(isolation, {'envs': env_config}, PluginManager()) + project_config = ProjectConfig(isolation, {"envs": env_config}, PluginManager()) expected_envs = { - 'default': {'type': 'virtual'}, - 'foo.9000': {'type': 'virtual', 'skip-install': False}, - 'foo.42': {'type': 'virtual', 'skip-install': False}, - 'foo.bar': {'type': 'virtual', 'skip-install': False}, + "default": {"type": "virtual"}, + "foo.9000": {"type": "virtual", "skip-install": False}, + "foo.42": {"type": "virtual", "skip-install": False}, + "foo.bar": {"type": "virtual", "skip-install": False}, } assert project_config.envs == expected_envs - assert project_config.matrices['foo'] == construct_matrix_data('foo', env_config, {'skip-install': False}) + assert project_config.matrices["foo"] == construct_matrix_data("foo", env_config, {"skip-install": False}) def test_overrides_matrix_precedence_over_platform(self, isolation, current_platform): env_config = { - 'foo': { - 'skip-install': False, - 'matrix': [{'version': ['9000', '42']}, {'feature': ['bar']}], - 'overrides': { - 'platform': {current_platform: {'skip-install': True}}, - 'matrix': {'version': {'skip-install': [{'value': False, 'if': ['42']}]}}, + "foo": { + "skip-install": False, + "matrix": [{"version": ["9000", "42"]}, {"feature": ["bar"]}], + "overrides": { + "platform": {current_platform: {"skip-install": True}}, + "matrix": {"version": {"skip-install": [{"value": False, "if": ["42"]}]}}, }, } } - project_config = ProjectConfig(isolation, {'envs': env_config}, PluginManager()) + project_config = ProjectConfig(isolation, {"envs": env_config}, PluginManager()) expected_envs = { - 'default': {'type': 'virtual'}, - 'foo.9000': {'type': 'virtual', 'skip-install': True}, - 'foo.42': {'type': 'virtual', 'skip-install': False}, - 'foo.bar': {'type': 'virtual', 'skip-install': True}, + "default": {"type": "virtual"}, + "foo.9000": {"type": "virtual", "skip-install": True}, + "foo.42": {"type": "virtual", "skip-install": False}, + "foo.bar": {"type": "virtual", "skip-install": True}, } assert project_config.envs == expected_envs - assert project_config.matrices['foo'] == construct_matrix_data('foo', env_config, {'skip-install': True}) + assert project_config.matrices["foo"] == construct_matrix_data("foo", env_config, {"skip-install": True}) def test_overrides_matrix_precedence_over_env(self, isolation): - env_var = 'OVERRIDES_ENV_FOO' - env_config = { - 'foo': { - 'skip-install': False, - 'matrix': [{'version': ['9000', '42']}, {'feature': ['bar']}], - 'overrides': { - 'env': {env_var: {'skip-install': True}}, - 'matrix': {'version': {'skip-install': [{'value': False, 'if': ['42']}]}}, + env_var = "OVERRIDES_ENV_FOO" + env_config = { + "foo": { + "skip-install": False, + "matrix": [{"version": ["9000", "42"]}, {"feature": ["bar"]}], + "overrides": { + "env": {env_var: {"skip-install": True}}, + "matrix": {"version": {"skip-install": [{"value": False, "if": ["42"]}]}}, }, } } - project_config = ProjectConfig(isolation, {'envs': env_config}, PluginManager()) + project_config = ProjectConfig(isolation, {"envs": env_config}, PluginManager()) expected_envs = { - 'default': {'type': 'virtual'}, - 'foo.9000': {'type': 'virtual', 'skip-install': True}, - 'foo.42': {'type': 'virtual', 'skip-install': False}, - 'foo.bar': {'type': 'virtual', 'skip-install': True}, + "default": {"type": "virtual"}, + "foo.9000": {"type": "virtual", "skip-install": True}, + "foo.42": {"type": "virtual", "skip-install": False}, + "foo.bar": {"type": "virtual", "skip-install": True}, } - with EnvVars({env_var: 'any'}): + with EnvVars({env_var: "any"}): assert project_config.envs == expected_envs - assert project_config.matrices['foo'] == construct_matrix_data('foo', env_config, {'skip-install': True}) + assert project_config.matrices["foo"] == construct_matrix_data("foo", env_config, {"skip-install": True}) def test_overrides_env_precedence_over_platform(self, isolation, current_platform): - env_var = 'OVERRIDES_ENV_FOO' + env_var = "OVERRIDES_ENV_FOO" env_config = { - 'foo': { - 'overrides': { - 'platform': {current_platform: {'skip-install': True}}, - 'env': {env_var: {'skip-install': [{'value': False, 'if': ['foo']}]}}, + "foo": { + "overrides": { + "platform": {current_platform: {"skip-install": True}}, + "env": {env_var: {"skip-install": [{"value": False, "if": ["foo"]}]}}, }, } } - project_config = ProjectConfig(isolation, {'envs': env_config}, PluginManager()) + project_config = ProjectConfig(isolation, {"envs": env_config}, PluginManager()) expected_envs = { - 'default': {'type': 'virtual'}, - 'foo': {'type': 'virtual', 'skip-install': False}, + "default": {"type": "virtual"}, + "foo": {"type": "virtual", "skip-install": False}, } - with EnvVars({env_var: 'foo'}): + with EnvVars({env_var: "foo"}): assert project_config.envs == expected_envs # Test for options defined by environment plugins def test_overrides_for_environment_plugins(self, isolation, current_platform): - env_var = 'OVERRIDES_ENV_FOO' - env_config = { - 'foo': { - 'matrix': [{'version': ['9000']}, {'feature': ['bar']}], - 'overrides': { - 'platform': {current_platform: {'foo': True}}, - 'env': {env_var: {'bar': [{'value': 'foobar', 'if': ['foo']}]}}, - 'matrix': {'version': {'baz': 'BAR=ok'}}, + env_var = "OVERRIDES_ENV_FOO" + env_config = { + "foo": { + "matrix": [{"version": ["9000"]}, {"feature": ["bar"]}], + "overrides": { + "platform": {current_platform: {"foo": True}}, + "env": {env_var: {"bar": [{"value": "foobar", "if": ["foo"]}]}}, + "matrix": {"version": {"baz": "BAR=ok"}}, }, } } - project_config = ProjectConfig(isolation, {'envs': env_config}, PluginManager()) + project_config = ProjectConfig(isolation, {"envs": env_config}, PluginManager()) expected_envs = { - 'default': {'type': 'virtual'}, - 'foo.9000': {'type': 'virtual'}, - 'foo.bar': {'type': 'virtual'}, + "default": {"type": "virtual"}, + "foo.9000": {"type": "virtual"}, + "foo.bar": {"type": "virtual"}, } - with EnvVars({env_var: 'foo'}): + with EnvVars({env_var: "foo"}): assert project_config.envs == expected_envs - assert project_config.matrices['foo'] == construct_matrix_data('foo', env_config) + assert project_config.matrices["foo"] == construct_matrix_data("foo", env_config) - project_config.finalize_env_overrides({'foo': bool, 'bar': str, 'baz': dict}) + project_config.finalize_env_overrides({"foo": bool, "bar": str, "baz": dict}) expected_envs = { - 'default': {'type': 'virtual'}, - 'foo.9000': {'type': 'virtual', 'foo': True, 'bar': 'foobar', 'baz': {'BAR': 'ok'}}, - 'foo.bar': {'type': 'virtual', 'foo': True, 'bar': 'foobar'}, + "default": {"type": "virtual"}, + "foo.9000": {"type": "virtual", "foo": True, "bar": "foobar", "baz": {"BAR": "ok"}}, + "foo.bar": {"type": "virtual", "foo": True, "bar": "foobar"}, } assert project_config.envs == expected_envs - assert project_config.matrices['foo'] == construct_matrix_data('foo', env_config) + assert project_config.matrices["foo"] == construct_matrix_data("foo", env_config) # Test environment collectors def test_environment_collector_finalize_config(self, helpers, temp_dir): @@ -2515,25 +2515,25 @@ def finalize_config(self, config): ) env_config = { - 'foo': { - 'matrix': [{'version': ['9000', '42']}, {'feature': ['bar']}], - 'overrides': {'matrix': {'version': {'type': {'value': 'baz', 'if': ['42']}}}}, + "foo": { + "matrix": [{"version": ["9000", "42"]}, {"feature": ["bar"]}], + "overrides": {"matrix": {"version": {"type": {"value": "baz", "if": ["42"]}}}}, } } project_config = ProjectConfig( - temp_dir, {'envs': env_config, 'env': {'collectors': {'custom': {}}}}, PluginManager() + temp_dir, {"envs": env_config, "env": {"collectors": {"custom": {}}}}, PluginManager() ) expected_envs = { - 'default': {'type': 'foo'}, - 'foo.9000': {'type': 'foo'}, - 'foo.42': {'type': 'baz'}, - 'foo.bar': {'type': 'foo'}, + "default": {"type": "foo"}, + "foo.9000": {"type": "foo"}, + "foo.42": {"type": "baz"}, + "foo.bar": {"type": "foo"}, } with temp_dir.as_cwd(): assert project_config.envs == expected_envs - assert project_config.matrices['foo'] == construct_matrix_data('foo', env_config, {'type': 'foo'}) + assert project_config.matrices["foo"] == construct_matrix_data("foo", env_config, {"type": "foo"}) def test_environment_collector_finalize_environments(self, helpers, temp_dir): file_path = temp_dir / DEFAULT_CUSTOM_SCRIPT @@ -2550,35 +2550,35 @@ def finalize_environments(self, config): ) env_config = { - 'foo': { - 'matrix': [{'version': ['9000', '42']}, {'feature': ['bar']}], - 'overrides': {'matrix': {'version': {'type': {'value': 'baz', 'if': ['42']}}}}, + "foo": { + "matrix": [{"version": ["9000", "42"]}, {"feature": ["bar"]}], + "overrides": {"matrix": {"version": {"type": {"value": "baz", "if": ["42"]}}}}, } } project_config = ProjectConfig( - temp_dir, {'envs': env_config, 'env': {'collectors': {'custom': {}}}}, PluginManager() + temp_dir, {"envs": env_config, "env": {"collectors": {"custom": {}}}}, PluginManager() ) expected_envs = { - 'default': {'type': 'virtual'}, - 'foo.9000': {'type': 'virtual'}, - 'foo.42': {'type': 'foo'}, - 'foo.bar': {'type': 'virtual'}, + "default": {"type": "virtual"}, + "foo.9000": {"type": "virtual"}, + "foo.42": {"type": "foo"}, + "foo.bar": {"type": "virtual"}, } with temp_dir.as_cwd(): assert project_config.envs == expected_envs - assert project_config.matrices['foo'] == construct_matrix_data('foo', env_config) + assert project_config.matrices["foo"] == construct_matrix_data("foo", env_config) class TestPublish: def test_not_table(self, isolation): - with pytest.raises(TypeError, match='Field `tool.hatch.publish` must be a table'): - _ = ProjectConfig(isolation, {'publish': 9000}).publish + with pytest.raises(TypeError, match="Field `tool.hatch.publish` must be a table"): + _ = ProjectConfig(isolation, {"publish": 9000}).publish def test_config_not_table(self, isolation): - with pytest.raises(TypeError, match='Field `tool.hatch.publish.foo` must be a table'): - _ = ProjectConfig(isolation, {'publish': {'foo': 9000}}).publish + with pytest.raises(TypeError, match="Field `tool.hatch.publish.foo` must be a table"): + _ = ProjectConfig(isolation, {"publish": {"foo": 9000}}).publish def test_default(self, isolation): project_config = ProjectConfig(isolation, {}) @@ -2586,25 +2586,25 @@ def test_default(self, isolation): assert project_config.publish == project_config.publish == {} def test_defined(self, isolation): - project_config = ProjectConfig(isolation, {'publish': {'foo': {'bar': 'baz'}}}) + project_config = ProjectConfig(isolation, {"publish": {"foo": {"bar": "baz"}}}) - assert project_config.publish == {'foo': {'bar': 'baz'}} + assert project_config.publish == {"foo": {"bar": "baz"}} class TestScripts: def test_not_table(self, isolation): - config = {'scripts': 9000} + config = {"scripts": 9000} project_config = ProjectConfig(isolation, config) - with pytest.raises(TypeError, match='Field `tool.hatch.scripts` must be a table'): + with pytest.raises(TypeError, match="Field `tool.hatch.scripts` must be a table"): _ = project_config.scripts def test_name_contains_spaces(self, isolation): - config = {'scripts': {'foo bar': []}} + config = {"scripts": {"foo bar": []}} project_config = ProjectConfig(isolation, config) with pytest.raises( - ValueError, match='Script name `foo bar` in field `tool.hatch.scripts` must not contain spaces' + ValueError, match="Script name `foo bar` in field `tool.hatch.scripts` must not contain spaces" ): _ = project_config.scripts @@ -2614,228 +2614,228 @@ def test_default(self, isolation): assert project_config.scripts == project_config.scripts == {} def test_single_commands(self, isolation): - config = {'scripts': {'foo': 'command1', 'bar': 'command2'}} + config = {"scripts": {"foo": "command1", "bar": "command2"}} project_config = ProjectConfig(isolation, config) - assert project_config.scripts == {'foo': ['command1'], 'bar': ['command2']} + assert project_config.scripts == {"foo": ["command1"], "bar": ["command2"]} def test_multiple_commands(self, isolation): - config = {'scripts': {'foo': 'command1', 'bar': ['command3', 'command2']}} + config = {"scripts": {"foo": "command1", "bar": ["command3", "command2"]}} project_config = ProjectConfig(isolation, config) - assert project_config.scripts == {'foo': ['command1'], 'bar': ['command3', 'command2']} + assert project_config.scripts == {"foo": ["command1"], "bar": ["command3", "command2"]} def test_multiple_commands_not_string(self, isolation): - config = {'scripts': {'foo': [9000]}} + config = {"scripts": {"foo": [9000]}} project_config = ProjectConfig(isolation, config) - with pytest.raises(TypeError, match='Command #1 in field `tool.hatch.scripts.foo` must be a string'): + with pytest.raises(TypeError, match="Command #1 in field `tool.hatch.scripts.foo` must be a string"): _ = project_config.scripts def test_config_invalid_type(self, isolation): - config = {'scripts': {'foo': 9000}} + config = {"scripts": {"foo": 9000}} project_config = ProjectConfig(isolation, config) - with pytest.raises(TypeError, match='Field `tool.hatch.scripts.foo` must be a string or an array of strings'): + with pytest.raises(TypeError, match="Field `tool.hatch.scripts.foo` must be a string or an array of strings"): _ = project_config.scripts def test_command_expansion_basic(self, isolation): - config = {'scripts': {'foo': 'command1', 'bar': ['command3', 'foo']}} + config = {"scripts": {"foo": "command1", "bar": ["command3", "foo"]}} project_config = ProjectConfig(isolation, config) - assert project_config.scripts == {'foo': ['command1'], 'bar': ['command3', 'command1']} + assert project_config.scripts == {"foo": ["command1"], "bar": ["command3", "command1"]} def test_command_expansion_multiple_nested(self, isolation): config = { - 'scripts': { - 'foo': 'command3', - 'baz': ['command5', 'bar', 'foo', 'command1'], - 'bar': ['command4', 'foo', 'command2'], + "scripts": { + "foo": "command3", + "baz": ["command5", "bar", "foo", "command1"], + "bar": ["command4", "foo", "command2"], } } project_config = ProjectConfig(isolation, config) assert project_config.scripts == { - 'foo': ['command3'], - 'baz': ['command5', 'command4', 'command3', 'command2', 'command3', 'command1'], - 'bar': ['command4', 'command3', 'command2'], + "foo": ["command3"], + "baz": ["command5", "command4", "command3", "command2", "command3", "command1"], + "bar": ["command4", "command3", "command2"], } def test_command_expansion_multiple_nested_ignore_exit_code(self, isolation): config = { - 'scripts': { - 'foo': 'command3', - 'baz': ['command5', '- bar', 'foo', 'command1'], - 'bar': ['command4', '- foo', 'command2'], + "scripts": { + "foo": "command3", + "baz": ["command5", "- bar", "foo", "command1"], + "bar": ["command4", "- foo", "command2"], } } project_config = ProjectConfig(isolation, config) assert project_config.scripts == { - 'foo': ['command3'], - 'baz': ['command5', '- command4', '- command3', '- command2', 'command3', 'command1'], - 'bar': ['command4', '- command3', 'command2'], + "foo": ["command3"], + "baz": ["command5", "- command4", "- command3", "- command2", "command3", "command1"], + "bar": ["command4", "- command3", "command2"], } def test_command_expansion_modification(self, isolation): config = { - 'scripts': { - 'foo': 'command3', - 'baz': ['command5', 'bar world', 'foo', 'command1'], - 'bar': ['command4', 'foo hello', 'command2'], + "scripts": { + "foo": "command3", + "baz": ["command5", "bar world", "foo", "command1"], + "bar": ["command4", "foo hello", "command2"], } } project_config = ProjectConfig(isolation, config) assert project_config.scripts == { - 'foo': ['command3'], - 'baz': ['command5', 'command4 world', 'command3 hello world', 'command2 world', 'command3', 'command1'], - 'bar': ['command4', 'command3 hello', 'command2'], + "foo": ["command3"], + "baz": ["command5", "command4 world", "command3 hello world", "command2 world", "command3", "command1"], + "bar": ["command4", "command3 hello", "command2"], } def test_command_expansion_circular_inheritance(self, isolation): - config = {'scripts': {'foo': 'bar', 'bar': 'foo'}} + config = {"scripts": {"foo": "bar", "bar": "foo"}} project_config = ProjectConfig(isolation, config) with pytest.raises( - ValueError, match='Circular expansion detected for field `tool.hatch.scripts`: foo -> bar -> foo' + ValueError, match="Circular expansion detected for field `tool.hatch.scripts`: foo -> bar -> foo" ): _ = project_config.scripts class TestBuild: def test_not_table(self, isolation): - config = {'build': 9000} + config = {"build": 9000} project_config = ProjectConfig(isolation, config) - with pytest.raises(TypeError, match='Field `tool.hatch.build` must be a table'): + with pytest.raises(TypeError, match="Field `tool.hatch.build` must be a table"): _ = project_config.build def test_targets_not_table(self, isolation): - config = {'build': {'targets': 9000}} + config = {"build": {"targets": 9000}} project_config = ProjectConfig(isolation, config) - with pytest.raises(TypeError, match='Field `tool.hatch.build.targets` must be a table'): - _ = project_config.build.target('foo') + with pytest.raises(TypeError, match="Field `tool.hatch.build.targets` must be a table"): + _ = project_config.build.target("foo") def test_target_not_table(self, isolation): - config = {'build': {'targets': {'foo': 9000}}} + config = {"build": {"targets": {"foo": 9000}}} project_config = ProjectConfig(isolation, config) - with pytest.raises(TypeError, match='Field `tool.hatch.build.targets.foo` must be a table'): - _ = project_config.build.target('foo') + with pytest.raises(TypeError, match="Field `tool.hatch.build.targets.foo` must be a table"): + _ = project_config.build.target("foo") def test_directory_global_not_table(self, isolation): - config = {'build': {'directory': 9000}} + config = {"build": {"directory": 9000}} project_config = ProjectConfig(isolation, config) - with pytest.raises(TypeError, match='Field `tool.hatch.build.directory` must be a string'): - _ = project_config.build.target('foo').directory + with pytest.raises(TypeError, match="Field `tool.hatch.build.directory` must be a string"): + _ = project_config.build.target("foo").directory def test_directory_not_table(self, isolation): - config = {'build': {'targets': {'foo': {'directory': 9000}}}} + config = {"build": {"targets": {"foo": {"directory": 9000}}}} project_config = ProjectConfig(isolation, config) - with pytest.raises(TypeError, match='Field `tool.hatch.build.targets.foo.directory` must be a string'): - _ = project_config.build.target('foo').directory + with pytest.raises(TypeError, match="Field `tool.hatch.build.targets.foo.directory` must be a string"): + _ = project_config.build.target("foo").directory def test_directory_default(self, isolation): project_config = ProjectConfig(isolation, {}) - assert project_config.build.target('foo').directory == DEFAULT_BUILD_DIRECTORY + assert project_config.build.target("foo").directory == DEFAULT_BUILD_DIRECTORY def test_directory_global_correct(self, isolation): - config = {'build': {'directory': 'bar'}} + config = {"build": {"directory": "bar"}} project_config = ProjectConfig(isolation, config) - assert project_config.build.target('foo').directory == 'bar' + assert project_config.build.target("foo").directory == "bar" def test_directory_target_override(self, isolation): - config = {'build': {'directory': 'bar', 'targets': {'foo': {'directory': 'baz'}}}} + config = {"build": {"directory": "bar", "targets": {"foo": {"directory": "baz"}}}} project_config = ProjectConfig(isolation, config) - assert project_config.build.target('foo').directory == 'baz' + assert project_config.build.target("foo").directory == "baz" def test_dependencies_global_not_array(self, isolation): - config = {'build': {'dependencies': 9000}} + config = {"build": {"dependencies": 9000}} project_config = ProjectConfig(isolation, config) - with pytest.raises(TypeError, match='Field `tool.hatch.build.dependencies` must be an array'): - _ = project_config.build.target('foo').dependencies + with pytest.raises(TypeError, match="Field `tool.hatch.build.dependencies` must be an array"): + _ = project_config.build.target("foo").dependencies def test_dependencies_global_entry_not_string(self, isolation): - config = {'build': {'dependencies': [9000]}} + config = {"build": {"dependencies": [9000]}} project_config = ProjectConfig(isolation, config) - with pytest.raises(TypeError, match='Dependency #1 in field `tool.hatch.build.dependencies` must be a string'): - _ = project_config.build.target('foo').dependencies + with pytest.raises(TypeError, match="Dependency #1 in field `tool.hatch.build.dependencies` must be a string"): + _ = project_config.build.target("foo").dependencies def test_dependencies_not_array(self, isolation): - config = {'build': {'targets': {'foo': {'dependencies': 9000}}}} + config = {"build": {"targets": {"foo": {"dependencies": 9000}}}} project_config = ProjectConfig(isolation, config) - with pytest.raises(TypeError, match='Field `tool.hatch.build.targets.foo.dependencies` must be an array'): - _ = project_config.build.target('foo').dependencies + with pytest.raises(TypeError, match="Field `tool.hatch.build.targets.foo.dependencies` must be an array"): + _ = project_config.build.target("foo").dependencies def test_dependencies_entry_not_string(self, isolation): - config = {'build': {'targets': {'foo': {'dependencies': [9000]}}}} + config = {"build": {"targets": {"foo": {"dependencies": [9000]}}}} project_config = ProjectConfig(isolation, config) with pytest.raises( - TypeError, match='Dependency #1 in field `tool.hatch.build.targets.foo.dependencies` must be a string' + TypeError, match="Dependency #1 in field `tool.hatch.build.targets.foo.dependencies` must be a string" ): - _ = project_config.build.target('foo').dependencies + _ = project_config.build.target("foo").dependencies def test_dependencies_target_merge(self, isolation): - config = {'build': {'dependencies': ['baz'], 'targets': {'foo': {'dependencies': ['bar']}}}} + config = {"build": {"dependencies": ["baz"], "targets": {"foo": {"dependencies": ["bar"]}}}} project_config = ProjectConfig(isolation, config) - assert project_config.build.target('foo').dependencies == ['baz', 'bar'] + assert project_config.build.target("foo").dependencies == ["baz", "bar"] def test_hooks_global_not_table(self, isolation): - config = {'build': {'hooks': 9000}} + config = {"build": {"hooks": 9000}} project_config = ProjectConfig(isolation, config) - with pytest.raises(TypeError, match='Field `tool.hatch.build.hooks` must be a table'): - _ = project_config.build.target('foo').hook_config + with pytest.raises(TypeError, match="Field `tool.hatch.build.hooks` must be a table"): + _ = project_config.build.target("foo").hook_config def test_hook_config_global_not_table(self, isolation): - config = {'build': {'hooks': {'hook1': 9000}}} + config = {"build": {"hooks": {"hook1": 9000}}} project_config = ProjectConfig(isolation, config) - with pytest.raises(TypeError, match='Field `tool.hatch.build.hooks.hook1` must be a table'): - _ = project_config.build.target('foo').hook_config + with pytest.raises(TypeError, match="Field `tool.hatch.build.hooks.hook1` must be a table"): + _ = project_config.build.target("foo").hook_config def test_hooks_not_table(self, isolation): - config = {'build': {'targets': {'foo': {'hooks': 9000}}}} + config = {"build": {"targets": {"foo": {"hooks": 9000}}}} project_config = ProjectConfig(isolation, config) - with pytest.raises(TypeError, match='Field `tool.hatch.build.targets.foo.hooks` must be a table'): - _ = project_config.build.target('foo').hook_config + with pytest.raises(TypeError, match="Field `tool.hatch.build.targets.foo.hooks` must be a table"): + _ = project_config.build.target("foo").hook_config def test_hook_config_not_table(self, isolation): - config = {'build': {'targets': {'foo': {'hooks': {'hook1': 9000}}}}} + config = {"build": {"targets": {"foo": {"hooks": {"hook1": 9000}}}}} project_config = ProjectConfig(isolation, config) - with pytest.raises(TypeError, match='Field `tool.hatch.build.targets.foo.hooks.hook1` must be a table'): - _ = project_config.build.target('foo').hook_config + with pytest.raises(TypeError, match="Field `tool.hatch.build.targets.foo.hooks.hook1` must be a table"): + _ = project_config.build.target("foo").hook_config def test_hook_config_target_override(self, isolation): config = { - 'build': { - 'hooks': { - 'hook1': {'foo': 'bar', 'enable-by-default': False}, - 'hook2': {'foo': 'bar'}, - 'hook3': {'foo': 'bar'}, - 'hook4': {'foo': 'bar'}, + "build": { + "hooks": { + "hook1": {"foo": "bar", "enable-by-default": False}, + "hook2": {"foo": "bar"}, + "hook3": {"foo": "bar"}, + "hook4": {"foo": "bar"}, }, - 'targets': { - 'foo': { - 'hooks': { - 'hook3': {'bar': 'foo'}, - 'hook4': {'bar': 'foo', 'enable-by-default': False}, - 'hook5': {'bar': 'foo'}, - 'hook6': {'bar': 'foo', 'enable-by-default': False}, + "targets": { + "foo": { + "hooks": { + "hook3": {"bar": "foo"}, + "hook4": {"bar": "foo", "enable-by-default": False}, + "hook5": {"bar": "foo"}, + "hook6": {"bar": "foo", "enable-by-default": False}, }, }, }, @@ -2843,29 +2843,29 @@ def test_hook_config_target_override(self, isolation): } project_config = ProjectConfig(isolation, config) - hook_config = project_config.build.target('foo').hook_config + hook_config = project_config.build.target("foo").hook_config assert hook_config == { - 'hook2': {'foo': 'bar'}, - 'hook3': {'bar': 'foo'}, - 'hook5': {'bar': 'foo'}, + "hook2": {"foo": "bar"}, + "hook3": {"bar": "foo"}, + "hook5": {"bar": "foo"}, } def test_hook_config_all_enabled(self, isolation): config = { - 'build': { - 'hooks': { - 'hook1': {'foo': 'bar', 'enable-by-default': False}, - 'hook2': {'foo': 'bar'}, - 'hook3': {'foo': 'bar'}, - 'hook4': {'foo': 'bar'}, + "build": { + "hooks": { + "hook1": {"foo": "bar", "enable-by-default": False}, + "hook2": {"foo": "bar"}, + "hook3": {"foo": "bar"}, + "hook4": {"foo": "bar"}, }, - 'targets': { - 'foo': { - 'hooks': { - 'hook3': {'bar': 'foo'}, - 'hook4': {'bar': 'foo', 'enable-by-default': False}, - 'hook5': {'bar': 'foo'}, - 'hook6': {'bar': 'foo', 'enable-by-default': False}, + "targets": { + "foo": { + "hooks": { + "hook3": {"bar": "foo"}, + "hook4": {"bar": "foo", "enable-by-default": False}, + "hook5": {"bar": "foo"}, + "hook6": {"bar": "foo", "enable-by-default": False}, }, }, }, @@ -2873,34 +2873,34 @@ def test_hook_config_all_enabled(self, isolation): } project_config = ProjectConfig(isolation, config) - with EnvVars({BuildEnvVars.HOOKS_ENABLE: 'true'}): - hook_config = project_config.build.target('foo').hook_config + with EnvVars({BuildEnvVars.HOOKS_ENABLE: "true"}): + hook_config = project_config.build.target("foo").hook_config assert hook_config == { - 'hook1': {'foo': 'bar', 'enable-by-default': False}, - 'hook2': {'foo': 'bar'}, - 'hook3': {'bar': 'foo'}, - 'hook4': {'bar': 'foo', 'enable-by-default': False}, - 'hook5': {'bar': 'foo'}, - 'hook6': {'bar': 'foo', 'enable-by-default': False}, + "hook1": {"foo": "bar", "enable-by-default": False}, + "hook2": {"foo": "bar"}, + "hook3": {"bar": "foo"}, + "hook4": {"bar": "foo", "enable-by-default": False}, + "hook5": {"bar": "foo"}, + "hook6": {"bar": "foo", "enable-by-default": False}, } def test_hook_config_all_disabled(self, isolation): config = { - 'build': { - 'hooks': { - 'hook1': {'foo': 'bar', 'enable-by-default': False}, - 'hook2': {'foo': 'bar'}, - 'hook3': {'foo': 'bar'}, - 'hook4': {'foo': 'bar'}, + "build": { + "hooks": { + "hook1": {"foo": "bar", "enable-by-default": False}, + "hook2": {"foo": "bar"}, + "hook3": {"foo": "bar"}, + "hook4": {"foo": "bar"}, }, - 'targets': { - 'foo': { - 'hooks': { - 'hook3': {'bar': 'foo'}, - 'hook4': {'bar': 'foo', 'enable-by-default': False}, - 'hook5': {'bar': 'foo'}, - 'hook6': {'bar': 'foo', 'enable-by-default': False}, + "targets": { + "foo": { + "hooks": { + "hook3": {"bar": "foo"}, + "hook4": {"bar": "foo", "enable-by-default": False}, + "hook5": {"bar": "foo"}, + "hook6": {"bar": "foo", "enable-by-default": False}, }, }, }, @@ -2908,27 +2908,27 @@ def test_hook_config_all_disabled(self, isolation): } project_config = ProjectConfig(isolation, config) - with EnvVars({BuildEnvVars.NO_HOOKS: 'true'}): - hook_config = project_config.build.target('foo').hook_config + with EnvVars({BuildEnvVars.NO_HOOKS: "true"}): + hook_config = project_config.build.target("foo").hook_config assert not hook_config def test_hook_config_specific_enabled(self, isolation): config = { - 'build': { - 'hooks': { - 'hook1': {'foo': 'bar', 'enable-by-default': False}, - 'hook2': {'foo': 'bar'}, - 'hook3': {'foo': 'bar'}, - 'hook4': {'foo': 'bar'}, + "build": { + "hooks": { + "hook1": {"foo": "bar", "enable-by-default": False}, + "hook2": {"foo": "bar"}, + "hook3": {"foo": "bar"}, + "hook4": {"foo": "bar"}, }, - 'targets': { - 'foo': { - 'hooks': { - 'hook3': {'bar': 'foo'}, - 'hook4': {'bar': 'foo', 'enable-by-default': False}, - 'hook5': {'bar': 'foo'}, - 'hook6': {'bar': 'foo', 'enable-by-default': False}, + "targets": { + "foo": { + "hooks": { + "hook3": {"bar": "foo"}, + "hook4": {"bar": "foo", "enable-by-default": False}, + "hook5": {"bar": "foo"}, + "hook6": {"bar": "foo", "enable-by-default": False}, }, }, }, @@ -2936,12 +2936,12 @@ def test_hook_config_specific_enabled(self, isolation): } project_config = ProjectConfig(isolation, config) - with EnvVars({f'{BuildEnvVars.HOOK_ENABLE_PREFIX}HOOK6': 'true'}): - hook_config = project_config.build.target('foo').hook_config + with EnvVars({f"{BuildEnvVars.HOOK_ENABLE_PREFIX}HOOK6": "true"}): + hook_config = project_config.build.target("foo").hook_config assert hook_config == { - 'hook2': {'foo': 'bar'}, - 'hook3': {'bar': 'foo'}, - 'hook5': {'bar': 'foo'}, - 'hook6': {'bar': 'foo', 'enable-by-default': False}, + "hook2": {"foo": "bar"}, + "hook3": {"bar": "foo"}, + "hook5": {"bar": "foo"}, + "hook6": {"bar": "foo", "enable-by-default": False}, } diff --git a/tests/project/test_core.py b/tests/project/test_core.py index 541c1efd0..2a14f17ff 100644 --- a/tests/project/test_core.py +++ b/tests/project/test_core.py @@ -8,7 +8,7 @@ def test_no_project(self, temp_dir): project = Project(temp_dir) assert project.find_project_root() is None - @pytest.mark.parametrize('file_name', ['pyproject.toml', 'setup.py']) + @pytest.mark.parametrize("file_name", ["pyproject.toml", "setup.py"]) def test_direct(self, temp_dir, file_name): project = Project(temp_dir) @@ -17,24 +17,24 @@ def test_direct(self, temp_dir, file_name): assert project.find_project_root() == temp_dir - @pytest.mark.parametrize('file_name', ['pyproject.toml', 'setup.py']) + @pytest.mark.parametrize("file_name", ["pyproject.toml", "setup.py"]) def test_recurse(self, temp_dir, file_name): project = Project(temp_dir) project_file = temp_dir / file_name project_file.touch() - path = temp_dir / 'test' + path = temp_dir / "test" path.mkdir() assert project.find_project_root() == temp_dir - @pytest.mark.parametrize('file_name', ['pyproject.toml', 'setup.py']) + @pytest.mark.parametrize("file_name", ["pyproject.toml", "setup.py"]) def test_no_path(self, temp_dir, file_name): project_file = temp_dir / file_name project_file.touch() - path = temp_dir / 'test' + path = temp_dir / "test" project = Project(path) assert project.find_project_root() == temp_dir @@ -42,51 +42,51 @@ def test_no_path(self, temp_dir, file_name): class TestLoadProjectFromConfig: def test_no_project_no_project_dirs(self, config_file): - assert Project.from_config(config_file.model, 'foo') is None + assert Project.from_config(config_file.model, "foo") is None def test_project_empty_string(self, config_file, temp_dir): - config_file.model.projects[''] = str(temp_dir) - assert Project.from_config(config_file.model, '') is None + config_file.model.projects[""] = str(temp_dir) + assert Project.from_config(config_file.model, "") is None def test_project_basic_string(self, config_file, temp_dir): - config_file.model.projects = {'foo': str(temp_dir)} - project = Project.from_config(config_file.model, 'foo') - assert project.chosen_name == 'foo' + config_file.model.projects = {"foo": str(temp_dir)} + project = Project.from_config(config_file.model, "foo") + assert project.chosen_name == "foo" assert project.location == temp_dir def test_project_complex(self, config_file, temp_dir): - config_file.model.projects = {'foo': {'location': str(temp_dir)}} - project = Project.from_config(config_file.model, 'foo') - assert project.chosen_name == 'foo' + config_file.model.projects = {"foo": {"location": str(temp_dir)}} + project = Project.from_config(config_file.model, "foo") + assert project.chosen_name == "foo" assert project.location == temp_dir def test_project_complex_null_location(self, config_file): - config_file.model.projects = {'foo': {'location': ''}} - assert Project.from_config(config_file.model, 'foo') is None + config_file.model.projects = {"foo": {"location": ""}} + assert Project.from_config(config_file.model, "foo") is None def test_project_dirs(self, config_file, temp_dir): - path = temp_dir / 'foo' + path = temp_dir / "foo" path.mkdir() config_file.model.dirs.project = [str(temp_dir)] - project = Project.from_config(config_file.model, 'foo') - assert project.chosen_name == 'foo' + project = Project.from_config(config_file.model, "foo") + assert project.chosen_name == "foo" assert project.location == path def test_project_dirs_null_dir(self, config_file): - config_file.model.dirs.project = [''] - assert Project.from_config(config_file.model, 'foo') is None + config_file.model.dirs.project = [""] + assert Project.from_config(config_file.model, "foo") is None def test_project_dirs_not_directory(self, config_file, temp_dir): - path = temp_dir / 'foo' + path = temp_dir / "foo" path.touch() config_file.model.dirs.project = [str(temp_dir)] - assert Project.from_config(config_file.model, 'foo') is None + assert Project.from_config(config_file.model, "foo") is None class TestChosenName: def test_selected(self, temp_dir): - project = Project(temp_dir, name='foo') - assert project.chosen_name == 'foo' + project = Project(temp_dir, name="foo") + assert project.chosen_name == "foo" def test_cwd(self, temp_dir): project = Project(temp_dir) @@ -99,7 +99,7 @@ def test_no_project(self, temp_dir): assert project.location == temp_dir assert project.root is None - @pytest.mark.parametrize('file_name', ['pyproject.toml', 'setup.py']) + @pytest.mark.parametrize("file_name", ["pyproject.toml", "setup.py"]) def test_project(self, temp_dir, file_name): project_file = temp_dir / file_name project_file.touch() @@ -114,69 +114,69 @@ def test_missing(self, temp_dir): project = Project(temp_dir) project.find_project_root() - assert project.raw_config == {'project': {'name': temp_dir.name}} + assert project.raw_config == {"project": {"name": temp_dir.name}} def test_exists(self, temp_dir): - project_file = temp_dir / 'pyproject.toml' + project_file = temp_dir / "pyproject.toml" project_file.touch() project = Project(temp_dir) project.find_project_root() - config = {'project': {'name': 'foo'}, 'bar': 'baz'} + config = {"project": {"name": "foo"}, "bar": "baz"} project.save_config(config) assert project.raw_config == config def test_exists_without_project_table(self, temp_dir): - project_file = temp_dir / 'pyproject.toml' + project_file = temp_dir / "pyproject.toml" project_file.touch() project = Project(temp_dir) project.find_project_root() - assert project.raw_config == {'project': {'name': temp_dir.name}} + assert project.raw_config == {"project": {"name": temp_dir.name}} class TestEnsureCWD: def test_location_is_file(self, temp_dir, mocker): - script_path = temp_dir / 'script.py' + script_path = temp_dir / "script.py" script_path.touch() project = Project(script_path) project.find_project_root() with temp_dir.as_cwd(): - mocker.patch('hatch.utils.fs.Path.as_cwd', side_effect=Exception) + mocker.patch("hatch.utils.fs.Path.as_cwd", side_effect=Exception) with project.ensure_cwd() as cwd: assert cwd == temp_dir def test_cwd_is_location(self, temp_dir, mocker): - project_file = temp_dir / 'pyproject.toml' + project_file = temp_dir / "pyproject.toml" project_file.touch() project = Project(temp_dir) project.find_project_root() with temp_dir.as_cwd(): - mocker.patch('hatch.utils.fs.Path.as_cwd', side_effect=Exception) + mocker.patch("hatch.utils.fs.Path.as_cwd", side_effect=Exception) with project.ensure_cwd() as cwd: assert cwd == temp_dir def test_cwd_inside_location(self, temp_dir, mocker): - project_file = temp_dir / 'pyproject.toml' + project_file = temp_dir / "pyproject.toml" project_file.touch() project = Project(temp_dir) project.find_project_root() - subdir = temp_dir / 'subdir' + subdir = temp_dir / "subdir" subdir.mkdir() with subdir.as_cwd(): - mocker.patch('hatch.utils.fs.Path.as_cwd', side_effect=Exception) + mocker.patch("hatch.utils.fs.Path.as_cwd", side_effect=Exception) with project.ensure_cwd() as cwd: assert cwd == subdir def test_cwd_outside_location(self, temp_dir): - subdir = temp_dir / 'subdir' + subdir = temp_dir / "subdir" subdir.mkdir() - project_file = subdir / 'pyproject.toml' + project_file = subdir / "pyproject.toml" project_file.touch() project = Project(subdir) project.find_project_root() diff --git a/tests/project/test_frontend.py b/tests/project/test_frontend.py index f7570856c..efbe0779c 100644 --- a/tests/project/test_frontend.py +++ b/tests/project/test_frontend.py @@ -8,11 +8,11 @@ from hatchling.builders.constants import EDITABLES_REQUIREMENT from hatchling.metadata.spec import project_metadata_from_core_metadata -BACKENDS = [('hatchling', 'hatchling.build'), ('flit-core', 'flit_core.buildapi')] +BACKENDS = [("hatchling", "hatchling.build"), ("flit-core", "flit_core.buildapi")] class MockEnvironment(EnvironmentInterface): # no cov - PLUGIN_NAME = 'mock' + PLUGIN_NAME = "mock" def find(self): pass @@ -41,13 +41,13 @@ def sync_dependencies(self): class TestPrepareMetadata: @pytest.mark.parametrize( - ('backend_pkg', 'backend_api'), + ("backend_pkg", "backend_api"), [pytest.param(backend_pkg, backend_api, id=backend_pkg) for backend_pkg, backend_api in BACKENDS], ) def test_wheel(self, temp_dir, temp_dir_data, platform, global_application, backend_pkg, backend_api): - project_dir = temp_dir / 'project' + project_dir = temp_dir / "project" project_dir.mkdir() - (project_dir / 'pyproject.toml').write_text( + (project_dir / "pyproject.toml").write_text( f"""\ [build-system] requires = ["{backend_pkg}"] @@ -60,16 +60,16 @@ def test_wheel(self, temp_dir, temp_dir_data, platform, global_application, back """ ) - package_dir = project_dir / 'foo' + package_dir = project_dir / "foo" package_dir.mkdir() - (package_dir / '__init__.py').touch() + (package_dir / "__init__.py").touch() project = Project(project_dir) project.build_env = MockEnvironment( temp_dir, project.metadata, - 'default', - project.config.envs['default'], + "default", + project.config.envs["default"], {}, temp_dir_data, temp_dir_data, @@ -78,30 +78,30 @@ def test_wheel(self, temp_dir, temp_dir_data, platform, global_application, back global_application, ) - output_dir = temp_dir / 'output' + output_dir = temp_dir / "output" output_dir.mkdir() script = project.build_frontend.scripts.prepare_metadata( output_dir=str(output_dir), project_root=str(project_dir) ) - platform.check_command([sys.executable, '-c', script]) - work_dir = output_dir / 'work' - output = json.loads((output_dir / 'output.json').read_text()) - metadata_file = work_dir / output['return_val'] / 'METADATA' + platform.check_command([sys.executable, "-c", script]) + work_dir = output_dir / "work" + output = json.loads((output_dir / "output.json").read_text()) + metadata_file = work_dir / output["return_val"] / "METADATA" assert project_metadata_from_core_metadata(metadata_file.read_text()) == { - 'name': 'foo', - 'version': '9000.42', - 'description': 'text', + "name": "foo", + "version": "9000.42", + "description": "text", } @pytest.mark.parametrize( - ('backend_pkg', 'backend_api'), + ("backend_pkg", "backend_api"), [pytest.param(backend_pkg, backend_api, id=backend_pkg) for backend_pkg, backend_api in BACKENDS], ) def test_editable(self, temp_dir, temp_dir_data, platform, global_application, backend_pkg, backend_api): - project_dir = temp_dir / 'project' + project_dir = temp_dir / "project" project_dir.mkdir() - (project_dir / 'pyproject.toml').write_text( + (project_dir / "pyproject.toml").write_text( f"""\ [build-system] requires = ["{backend_pkg}"] @@ -114,16 +114,16 @@ def test_editable(self, temp_dir, temp_dir_data, platform, global_application, b """ ) - package_dir = project_dir / 'foo' + package_dir = project_dir / "foo" package_dir.mkdir() - (package_dir / '__init__.py').touch() + (package_dir / "__init__.py").touch() project = Project(project_dir) project.build_env = MockEnvironment( temp_dir, project.metadata, - 'default', - project.config.envs['default'], + "default", + project.config.envs["default"], {}, temp_dir_data, temp_dir_data, @@ -132,32 +132,32 @@ def test_editable(self, temp_dir, temp_dir_data, platform, global_application, b global_application, ) - output_dir = temp_dir / 'output' + output_dir = temp_dir / "output" output_dir.mkdir() script = project.build_frontend.scripts.prepare_metadata( output_dir=str(output_dir), project_root=str(project_dir), editable=True ) - platform.check_command([sys.executable, '-c', script]) - work_dir = output_dir / 'work' - output = json.loads((output_dir / 'output.json').read_text()) - metadata_file = work_dir / output['return_val'] / 'METADATA' + platform.check_command([sys.executable, "-c", script]) + work_dir = output_dir / "work" + output = json.loads((output_dir / "output.json").read_text()) + metadata_file = work_dir / output["return_val"] / "METADATA" assert project_metadata_from_core_metadata(metadata_file.read_text()) == { - 'name': 'foo', - 'version': '9000.42', - 'description': 'text', + "name": "foo", + "version": "9000.42", + "description": "text", } class TestBuildWheel: @pytest.mark.parametrize( - ('backend_pkg', 'backend_api'), + ("backend_pkg", "backend_api"), [pytest.param(backend_pkg, backend_api, id=backend_pkg) for backend_pkg, backend_api in BACKENDS], ) def test_standard(self, temp_dir, temp_dir_data, platform, global_application, backend_pkg, backend_api): - project_dir = temp_dir / 'project' + project_dir = temp_dir / "project" project_dir.mkdir() - (project_dir / 'pyproject.toml').write_text( + (project_dir / "pyproject.toml").write_text( f"""\ [build-system] requires = ["{backend_pkg}"] @@ -170,16 +170,16 @@ def test_standard(self, temp_dir, temp_dir_data, platform, global_application, b """ ) - package_dir = project_dir / 'foo' + package_dir = project_dir / "foo" package_dir.mkdir() - (package_dir / '__init__.py').touch() + (package_dir / "__init__.py").touch() project = Project(project_dir) project.build_env = MockEnvironment( temp_dir, project.metadata, - 'default', - project.config.envs['default'], + "default", + project.config.envs["default"], {}, temp_dir_data, temp_dir_data, @@ -188,26 +188,26 @@ def test_standard(self, temp_dir, temp_dir_data, platform, global_application, b global_application, ) - output_dir = temp_dir / 'output' + output_dir = temp_dir / "output" output_dir.mkdir() script = project.build_frontend.scripts.build_wheel(output_dir=str(output_dir), project_root=str(project_dir)) - platform.check_command([sys.executable, '-c', script]) - work_dir = output_dir / 'work' - output = json.loads((output_dir / 'output.json').read_text()) - wheel_path = work_dir / output['return_val'] + platform.check_command([sys.executable, "-c", script]) + work_dir = output_dir / "work" + output = json.loads((output_dir / "output.json").read_text()) + wheel_path = work_dir / output["return_val"] assert wheel_path.is_file() - assert wheel_path.name.startswith('foo-9000.42-') - assert wheel_path.name.endswith('.whl') + assert wheel_path.name.startswith("foo-9000.42-") + assert wheel_path.name.endswith(".whl") @pytest.mark.parametrize( - ('backend_pkg', 'backend_api'), + ("backend_pkg", "backend_api"), [pytest.param(backend_pkg, backend_api, id=backend_pkg) for backend_pkg, backend_api in BACKENDS], ) def test_editable(self, temp_dir, temp_dir_data, platform, global_application, backend_pkg, backend_api): - project_dir = temp_dir / 'project' + project_dir = temp_dir / "project" project_dir.mkdir() - (project_dir / 'pyproject.toml').write_text( + (project_dir / "pyproject.toml").write_text( f"""\ [build-system] requires = ["{backend_pkg}"] @@ -220,16 +220,16 @@ def test_editable(self, temp_dir, temp_dir_data, platform, global_application, b """ ) - package_dir = project_dir / 'foo' + package_dir = project_dir / "foo" package_dir.mkdir() - (package_dir / '__init__.py').touch() + (package_dir / "__init__.py").touch() project = Project(project_dir) project.build_env = MockEnvironment( temp_dir, project.metadata, - 'default', - project.config.envs['default'], + "default", + project.config.envs["default"], {}, temp_dir_data, temp_dir_data, @@ -238,30 +238,30 @@ def test_editable(self, temp_dir, temp_dir_data, platform, global_application, b global_application, ) - output_dir = temp_dir / 'output' + output_dir = temp_dir / "output" output_dir.mkdir() script = project.build_frontend.scripts.build_wheel( output_dir=str(output_dir), project_root=str(project_dir), editable=True ) - platform.check_command([sys.executable, '-c', script]) - work_dir = output_dir / 'work' - output = json.loads((output_dir / 'output.json').read_text()) - wheel_path = work_dir / output['return_val'] + platform.check_command([sys.executable, "-c", script]) + work_dir = output_dir / "work" + output = json.loads((output_dir / "output.json").read_text()) + wheel_path = work_dir / output["return_val"] assert wheel_path.is_file() - assert wheel_path.name.startswith('foo-9000.42-') - assert wheel_path.name.endswith('.whl') + assert wheel_path.name.startswith("foo-9000.42-") + assert wheel_path.name.endswith(".whl") class TestSourceDistribution: @pytest.mark.parametrize( - ('backend_pkg', 'backend_api'), + ("backend_pkg", "backend_api"), [pytest.param(backend_pkg, backend_api, id=backend_pkg) for backend_pkg, backend_api in BACKENDS], ) def test_standard(self, temp_dir, temp_dir_data, platform, global_application, backend_pkg, backend_api): - project_dir = temp_dir / 'project' + project_dir = temp_dir / "project" project_dir.mkdir() - (project_dir / 'pyproject.toml').write_text( + (project_dir / "pyproject.toml").write_text( f"""\ [build-system] requires = ["{backend_pkg}"] @@ -274,16 +274,16 @@ def test_standard(self, temp_dir, temp_dir_data, platform, global_application, b """ ) - package_dir = project_dir / 'foo' + package_dir = project_dir / "foo" package_dir.mkdir() - (package_dir / '__init__.py').touch() + (package_dir / "__init__.py").touch() project = Project(project_dir) project.build_env = MockEnvironment( temp_dir, project.metadata, - 'default', - project.config.envs['default'], + "default", + project.config.envs["default"], {}, temp_dir_data, temp_dir_data, @@ -292,28 +292,28 @@ def test_standard(self, temp_dir, temp_dir_data, platform, global_application, b global_application, ) - output_dir = temp_dir / 'output' + output_dir = temp_dir / "output" output_dir.mkdir() script = project.build_frontend.scripts.build_sdist(output_dir=str(output_dir), project_root=str(project_dir)) - platform.check_command([sys.executable, '-c', script]) - work_dir = output_dir / 'work' - output = json.loads((output_dir / 'output.json').read_text()) - sdist_path = work_dir / output['return_val'] + platform.check_command([sys.executable, "-c", script]) + work_dir = output_dir / "work" + output = json.loads((output_dir / "output.json").read_text()) + sdist_path = work_dir / output["return_val"] assert sdist_path.is_file() - assert sdist_path.name == 'foo-9000.42.tar.gz' + assert sdist_path.name == "foo-9000.42.tar.gz" class TestGetRequires: @pytest.mark.parametrize( - ('backend_pkg', 'backend_api'), + ("backend_pkg", "backend_api"), [pytest.param(backend_pkg, backend_api, id=backend_pkg) for backend_pkg, backend_api in BACKENDS], ) - @pytest.mark.parametrize('build', ['sdist', 'wheel', 'editable']) + @pytest.mark.parametrize("build", ["sdist", "wheel", "editable"]) def test_default(self, temp_dir, temp_dir_data, platform, global_application, backend_pkg, backend_api, build): - project_dir = temp_dir / 'project' + project_dir = temp_dir / "project" project_dir.mkdir() - (project_dir / 'pyproject.toml').write_text( + (project_dir / "pyproject.toml").write_text( f"""\ [build-system] requires = ["{backend_pkg}"] @@ -326,16 +326,16 @@ def test_default(self, temp_dir, temp_dir_data, platform, global_application, ba """ ) - package_dir = project_dir / 'foo' + package_dir = project_dir / "foo" package_dir.mkdir() - (package_dir / '__init__.py').touch() + (package_dir / "__init__.py").touch() project = Project(project_dir) project.build_env = MockEnvironment( temp_dir, project.metadata, - 'default', - project.config.envs['default'], + "default", + project.config.envs["default"], {}, temp_dir_data, temp_dir_data, @@ -344,24 +344,24 @@ def test_default(self, temp_dir, temp_dir_data, platform, global_application, ba global_application, ) - output_dir = temp_dir / 'output' + output_dir = temp_dir / "output" output_dir.mkdir() script = project.build_frontend.scripts.get_requires( output_dir=str(output_dir), project_root=str(project_dir), build=build ) - platform.check_command([sys.executable, '-c', script]) - output = json.loads((output_dir / 'output.json').read_text()) + platform.check_command([sys.executable, "-c", script]) + output = json.loads((output_dir / "output.json").read_text()) - assert output['return_val'] == ( - [EDITABLES_REQUIREMENT] if backend_pkg == 'hatchling' and build == 'editable' else [] + assert output["return_val"] == ( + [EDITABLES_REQUIREMENT] if backend_pkg == "hatchling" and build == "editable" else [] ) class TestHatchGetBuildDeps: def test_default(self, temp_dir, temp_dir_data, platform, global_application): - project_dir = temp_dir / 'project' + project_dir = temp_dir / "project" project_dir.mkdir() - (project_dir / 'pyproject.toml').write_text( + (project_dir / "pyproject.toml").write_text( """\ [build-system] requires = ["hatchling"] @@ -373,16 +373,16 @@ def test_default(self, temp_dir, temp_dir_data, platform, global_application): """ ) - package_dir = project_dir / 'foo' + package_dir = project_dir / "foo" package_dir.mkdir() - (package_dir / '__init__.py').touch() + (package_dir / "__init__.py").touch() project = Project(project_dir) project.build_env = MockEnvironment( temp_dir, project.metadata, - 'default', - project.config.envs['default'], + "default", + project.config.envs["default"], {}, temp_dir_data, temp_dir_data, @@ -391,12 +391,12 @@ def test_default(self, temp_dir, temp_dir_data, platform, global_application): global_application, ) - output_dir = temp_dir / 'output' + output_dir = temp_dir / "output" output_dir.mkdir() script = project.build_frontend.hatch.scripts.get_build_deps( - output_dir=str(output_dir), project_root=str(project_dir), targets=['sdist', 'wheel'] + output_dir=str(output_dir), project_root=str(project_dir), targets=["sdist", "wheel"] ) - platform.check_command([sys.executable, '-c', script]) - output = json.loads((output_dir / 'output.json').read_text()) + platform.check_command([sys.executable, "-c", script]) + output = json.loads((output_dir / "output.json").read_text()) assert output == [] diff --git a/tests/project/test_utils.py b/tests/project/test_utils.py index 42d1dc155..818541c15 100644 --- a/tests/project/test_utils.py +++ b/tests/project/test_utils.py @@ -5,7 +5,7 @@ class TestParseInlineScriptMetadata: def test_no_metadata(self): - assert parse_inline_script_metadata('') is None + assert parse_inline_script_metadata("") is None def test_too_many_blocks(self, helpers): script = helpers.dedent( @@ -19,7 +19,7 @@ def test_too_many_blocks(self, helpers): # /// """ ) - with pytest.raises(ValueError, match='^Multiple inline metadata blocks found for type: script$'): + with pytest.raises(ValueError, match="^Multiple inline metadata blocks found for type: script$"): parse_inline_script_metadata(script) def test_correct(self, helpers): @@ -37,7 +37,7 @@ def test_correct(self, helpers): """ ) assert parse_inline_script_metadata(script) == { - 'embedded-csharp': helpers.dedent( + "embedded-csharp": helpers.dedent( """ /// /// text diff --git a/tests/publish/plugin/test_interface.py b/tests/publish/plugin/test_interface.py index ae412c565..06afb4b5f 100644 --- a/tests/publish/plugin/test_interface.py +++ b/tests/publish/plugin/test_interface.py @@ -4,7 +4,7 @@ class MockPublisher(PublisherInterface): # no cov - PLUGIN_NAME = 'mock' + PLUGIN_NAME = "mock" def publish(self, artifacts, options): pass @@ -19,38 +19,38 @@ def test_default(self, isolation): assert publisher.disable is publisher.disable is False def test_project_config(self, isolation): - project_config = {'disable': True} + project_config = {"disable": True} plugin_config = {} publisher = MockPublisher(None, isolation, None, project_config, plugin_config) assert publisher.disable is True def test_project_config_not_boolean(self, isolation): - project_config = {'disable': 9000} + project_config = {"disable": 9000} plugin_config = {} publisher = MockPublisher(None, isolation, None, project_config, plugin_config) - with pytest.raises(TypeError, match='Field `tool.hatch.publish.mock.disable` must be a boolean'): + with pytest.raises(TypeError, match="Field `tool.hatch.publish.mock.disable` must be a boolean"): _ = publisher.disable def test_plugin_config(self, isolation): project_config = {} - plugin_config = {'disable': True} + plugin_config = {"disable": True} publisher = MockPublisher(None, isolation, None, project_config, plugin_config) assert publisher.disable is True def test_plugin_config_not_boolean(self, isolation): project_config = {} - plugin_config = {'disable': 9000} + plugin_config = {"disable": 9000} publisher = MockPublisher(None, isolation, None, project_config, plugin_config) - with pytest.raises(TypeError, match='Global plugin configuration `publish.mock.disable` must be a boolean'): + with pytest.raises(TypeError, match="Global plugin configuration `publish.mock.disable` must be a boolean"): _ = publisher.disable def test_project_config_overrides_plugin_config(self, isolation): - project_config = {'disable': False} - plugin_config = {'disable': True} + project_config = {"disable": False} + plugin_config = {"disable": True} publisher = MockPublisher(None, isolation, None, project_config, plugin_config) assert publisher.disable is False diff --git a/tests/python/test_core.py b/tests/python/test_core.py index 1b0eae919..c4ccd5101 100644 --- a/tests/python/test_core.py +++ b/tests/python/test_core.py @@ -9,51 +9,51 @@ from hatch.utils.structures import EnvVars -@pytest.mark.parametrize('name', ORDERED_DISTRIBUTIONS) +@pytest.mark.parametrize("name", ORDERED_DISTRIBUTIONS) def test_custom_source(platform, current_arch, name): - if platform.name == 'macos' and current_arch == 'arm64' and name == '3.7': - pytest.skip('No macOS 3.7 distribution for ARM') + if platform.name == "macos" and current_arch == "arm64" and name == "3.7": + pytest.skip("No macOS 3.7 distribution for ARM") dist = get_distribution(name) - with EnvVars({custom_env_var(PythonEnvVars.CUSTOM_SOURCE_PREFIX, name): 'foo'}): - assert dist.source == 'foo' + with EnvVars({custom_env_var(PythonEnvVars.CUSTOM_SOURCE_PREFIX, name): "foo"}): + assert dist.source == "foo" @pytest.mark.requires_internet -@pytest.mark.parametrize('name', ORDERED_DISTRIBUTIONS) +@pytest.mark.parametrize("name", ORDERED_DISTRIBUTIONS) def test_installation(temp_dir, platform, current_arch, name): - if platform.name == 'macos' and current_arch == 'arm64' and name == '3.7': - pytest.skip('No macOS 3.7 distribution for ARM') + if platform.name == "macos" and current_arch == "arm64" and name == "3.7": + pytest.skip("No macOS 3.7 distribution for ARM") # Ensure the source and any parent directories get created - manager = PythonManager(temp_dir / 'foo' / 'bar') + manager = PythonManager(temp_dir / "foo" / "bar") dist = manager.install(name) python_path = dist.python_path assert python_path.is_file() - output = platform.check_command_output([python_path, '-c', 'import sys;print(sys.executable)']).strip() + output = platform.check_command_output([python_path, "-c", "import sys;print(sys.executable)"]).strip() assert output == str(python_path) - major_minor = name.replace('pypy', '') + major_minor = name.replace("pypy", "") - output = platform.check_command_output([python_path, '--version']).strip() + output = platform.check_command_output([python_path, "--version"]).strip() - assert output.startswith(f'Python {major_minor}.') - if name.startswith('pypy'): - assert 'PyPy' in output + assert output.startswith(f"Python {major_minor}.") + if name.startswith("pypy"): + assert "PyPy" in output class TestGetInstalled: def test_source_does_not_exist(self, temp_dir): - manager = PythonManager(temp_dir / 'foo') + manager = PythonManager(temp_dir / "foo") assert manager.get_installed() == {} def test_not_a_directory(self, temp_dir): manager = PythonManager(temp_dir) - dist = get_distribution('3.10') + dist = get_distribution("3.10") path = temp_dir / dist.name path.touch() @@ -62,7 +62,7 @@ def test_not_a_directory(self, temp_dir): def test_no_metadata_file(self, temp_dir): manager = PythonManager(temp_dir) - dist = get_distribution('3.10') + dist = get_distribution("3.10") path = temp_dir / dist.name path.mkdir() @@ -71,11 +71,11 @@ def test_no_metadata_file(self, temp_dir): def test_no_python_path(self, temp_dir): manager = PythonManager(temp_dir) - dist = get_distribution('3.10') + dist = get_distribution("3.10") path = temp_dir / dist.name path.mkdir() metadata_file = path / InstalledDistribution.metadata_filename() - metadata_file.write_text(json.dumps({'source': dist.source})) + metadata_file.write_text(json.dumps({"source": dist.source})) assert manager.get_installed() == {} @@ -87,7 +87,7 @@ def test_order(self, temp_dir, compatible_python_distributions): path = temp_dir / dist.name path.mkdir() metadata_file = path / InstalledDistribution.metadata_filename() - metadata_file.write_text(json.dumps({'source': dist.source})) + metadata_file.write_text(json.dumps({"source": dist.source})) python_path = path / dist.python_path python_path.parent.ensure_dir_exists() python_path.touch() diff --git a/tests/python/test_resolve.py b/tests/python/test_resolve.py index 394d324ad..6b2bb511a 100644 --- a/tests/python/test_resolve.py +++ b/tests/python/test_resolve.py @@ -11,133 +11,136 @@ class TestErrors: def test_unknown_distribution(self): - with pytest.raises(PythonDistributionUnknownError, match='Unknown distribution: foo'): - get_distribution('foo') + with pytest.raises(PythonDistributionUnknownError, match="Unknown distribution: foo"): + get_distribution("foo") @pytest.mark.skipif( - not (sys.platform == 'linux' and machine().lower() == 'x86_64'), - reason='No variants for this platform and architecture combination', + not (sys.platform == "linux" and machine().lower() == "x86_64"), + reason="No variants for this platform and architecture combination", ) def test_resolution_error(self, platform): - with EnvVars({'HATCH_PYTHON_VARIANT_CPU': 'foo'}), pytest.raises( - PythonDistributionResolutionError, - match=f"Could not find a default source for name='3.11' system='{platform.name}' arch=", + with ( + EnvVars({"HATCH_PYTHON_VARIANT_CPU": "foo"}), + pytest.raises( + PythonDistributionResolutionError, + match=f"Could not find a default source for name='3.11' system='{platform.name}' arch=", + ), ): - get_distribution('3.11') + get_distribution("3.11") class TestDistributionVersions: def test_cpython_standalone(self): - url = 'https://github.com/indygreg/python-build-standalone/releases/download/20230507/cpython-3.11.3%2B20230507-aarch64-unknown-linux-gnu-install_only.tar.gz' - dist = get_distribution('3.11', url) + url = "https://github.com/indygreg/python-build-standalone/releases/download/20230507/cpython-3.11.3%2B20230507-aarch64-unknown-linux-gnu-install_only.tar.gz" + dist = get_distribution("3.11", url) version = dist.version assert version.epoch == 0 - assert version.base_version == '3.11.3' + assert version.base_version == "3.11.3" def test_cpython_standalone_custom(self): - name = '3.11' + name = "3.11" dist = get_distribution(name) - with EnvVars({custom_env_var(PythonEnvVars.CUSTOM_VERSION_PREFIX, name): '9000.42'}): + with EnvVars({custom_env_var(PythonEnvVars.CUSTOM_VERSION_PREFIX, name): "9000.42"}): version = dist.version assert version.epoch == 100 - assert '.'.join(map(str, version.release)) == '9000.42' + assert ".".join(map(str, version.release)) == "9000.42" def test_pypy(self): - url = 'https://downloads.python.org/pypy/pypy3.10-v7.3.12-aarch64.tar.bz2' - dist = get_distribution('pypy3.10', url) + url = "https://downloads.python.org/pypy/pypy3.10-v7.3.12-aarch64.tar.bz2" + dist = get_distribution("pypy3.10", url) version = dist.version assert version.epoch == 0 - assert version.base_version == '7.3.12' + assert version.base_version == "7.3.12" def test_pypy_custom(self): - name = 'pypy3.10' + name = "pypy3.10" dist = get_distribution(name) - with EnvVars({custom_env_var(PythonEnvVars.CUSTOM_VERSION_PREFIX, name): '9000.42'}): + with EnvVars({custom_env_var(PythonEnvVars.CUSTOM_VERSION_PREFIX, name): "9000.42"}): version = dist.version assert version.epoch == 100 - assert '.'.join(map(str, version.release)) == '9000.42' + assert ".".join(map(str, version.release)) == "9000.42" class TestDistributionPaths: def test_cpython_standalone_custom(self): - name = '3.11' + name = "3.11" dist = get_distribution(name) - with EnvVars({custom_env_var(PythonEnvVars.CUSTOM_PATH_PREFIX, name): 'foo/bar/python'}): - assert dist.python_path == 'foo/bar/python' + with EnvVars({custom_env_var(PythonEnvVars.CUSTOM_PATH_PREFIX, name): "foo/bar/python"}): + assert dist.python_path == "foo/bar/python" def test_pypy_custom(self): - name = 'pypy3.10' + name = "pypy3.10" dist = get_distribution(name) - with EnvVars({custom_env_var(PythonEnvVars.CUSTOM_PATH_PREFIX, name): 'foo/bar/python'}): - assert dist.python_path == 'foo/bar/python' + with EnvVars({custom_env_var(PythonEnvVars.CUSTOM_PATH_PREFIX, name): "foo/bar/python"}): + assert dist.python_path == "foo/bar/python" @pytest.mark.requires_linux class TestVariantCPU: def test_legacy_option(self, current_arch): - variant = 'v4' - with EnvVars({'HATCH_PYTHON_VARIANT_LINUX': variant}): - dist = get_distribution('3.12') + variant = "v4" + with EnvVars({"HATCH_PYTHON_VARIANT_LINUX": variant}): + dist = get_distribution("3.12") - if current_arch != 'x86_64': + if current_arch != "x86_64": assert variant not in dist.source else: assert variant in dist.source - @pytest.mark.parametrize('variant', ['v1', 'v2', 'v3', 'v4']) + @pytest.mark.parametrize("variant", ["v1", "v2", "v3", "v4"]) def test_compatibility(self, variant, current_arch): - with EnvVars({'HATCH_PYTHON_VARIANT_CPU': variant}): - dist = get_distribution('3.12') + with EnvVars({"HATCH_PYTHON_VARIANT_CPU": variant}): + dist = get_distribution("3.12") - if current_arch != 'x86_64' or variant == 'v1': + if current_arch != "x86_64" or variant == "v1": assert variant not in dist.source else: assert variant in dist.source @pytest.mark.skipif( - machine().lower() != 'x86_64', - reason='No variants for this platform and architecture combination', + machine().lower() != "x86_64", + reason="No variants for this platform and architecture combination", ) @pytest.mark.parametrize( - ('variant', 'flags'), + ("variant", "flags"), [ pytest.param( - 'v1', + "v1", # Just guessing here... - 'flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ht tm pbe syscall nx rdtscp lm constant_tsc arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc cpuid aperfmperf pni dtes64 monitor ds_cpl smx est tm2', - id='v1', + "flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ht tm pbe syscall nx rdtscp lm constant_tsc arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc cpuid aperfmperf pni dtes64 monitor ds_cpl smx est tm2", + id="v1", ), pytest.param( - 'v2', + "v2", # Intel Core i7-860 - 'flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ht tm pbe syscall nx rdtscp lm constant_tsc arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc cpuid aperfmperf pni dtes64 monitor ds_cpl smx est tm2 ssse3 cx16 xtpr pdcm sse4_1 sse4_2 popcnt lahf_lm pti ssbd ibrs ibpb stibp dtherm ida flush_l1d', - id='v2', + "flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ht tm pbe syscall nx rdtscp lm constant_tsc arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc cpuid aperfmperf pni dtes64 monitor ds_cpl smx est tm2 ssse3 cx16 xtpr pdcm sse4_1 sse4_2 popcnt lahf_lm pti ssbd ibrs ibpb stibp dtherm ida flush_l1d", + id="v2", ), pytest.param( - 'v3', + "v3", # Intel Core i5-5300U - 'flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc cpuid aperfmperf pni pclmulqdq dtes64 monitor ds_cpl vmx smx est tm2 ssse3 sdbg fma cx16 xtpr pdcm pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm abm 3dnowprefetch cpuid_fault epb invpcid_single pti ssbd ibrs ibpb stibp tpr_shadow flexpriority ept vpid ept_ad fsgsbase tsc_adjust bmi1 hle avx2 smep bmi2 erms invpcid rtm rdseed adx smap intel_pt xsaveopt dtherm ida arat pln pts vnmi md_clear flush_l1d', - id='v3', + "flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc cpuid aperfmperf pni pclmulqdq dtes64 monitor ds_cpl vmx smx est tm2 ssse3 sdbg fma cx16 xtpr pdcm pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm abm 3dnowprefetch cpuid_fault epb invpcid_single pti ssbd ibrs ibpb stibp tpr_shadow flexpriority ept vpid ept_ad fsgsbase tsc_adjust bmi1 hle avx2 smep bmi2 erms invpcid rtm rdseed adx smap intel_pt xsaveopt dtherm ida arat pln pts vnmi md_clear flush_l1d", + id="v3", ), pytest.param( - 'v4', + "v4", # Just guessing here... - 'flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc cpuid aperfmperf pni pclmulqdq dtes64 monitor ds_cpl vmx smx est tm2 ssse3 sdbg fma cx16 xtpr pdcm pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm abm 3dnowprefetch cpuid_fault epb invpcid_single pti ssbd ibrs ibpb stibp tpr_shadow flexpriority ept vpid ept_ad fsgsbase tsc_adjust bmi1 hle avx2 smep bmi2 erms invpcid rtm rdseed adx smap intel_pt xsaveopt dtherm ida arat pln pts vnmi md_clear flush_l1d avx512f avx512bw avx512cd avx512dq avx512vl', - id='v4', + "flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc cpuid aperfmperf pni pclmulqdq dtes64 monitor ds_cpl vmx smx est tm2 ssse3 sdbg fma cx16 xtpr pdcm pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm abm 3dnowprefetch cpuid_fault epb invpcid_single pti ssbd ibrs ibpb stibp tpr_shadow flexpriority ept vpid ept_ad fsgsbase tsc_adjust bmi1 hle avx2 smep bmi2 erms invpcid rtm rdseed adx smap intel_pt xsaveopt dtherm ida arat pln pts vnmi md_clear flush_l1d avx512f avx512bw avx512cd avx512dq avx512vl", + id="v4", ), ], ) def test_guess_variant(self, fs, variant, flags): - fs.create_file('/proc/cpuinfo', contents=flags) + fs.create_file("/proc/cpuinfo", contents=flags) - with EnvVars({'HATCH_PYTHON_VARIANT_CPU': ''}): - dist = get_distribution('3.12') - if variant == 'v1': - for v in ('v1', 'v2', 'v3', 'v4'): + with EnvVars({"HATCH_PYTHON_VARIANT_CPU": ""}): + dist = get_distribution("3.12") + if variant == "v1": + for v in ("v1", "v2", "v3", "v4"): assert v not in dist.source else: assert variant in dist.source @@ -145,14 +148,17 @@ def test_guess_variant(self, fs, variant, flags): class TestVariantGIL: def test_compatible(self): - with EnvVars({'HATCH_PYTHON_VARIANT_GIL': 'freethreaded'}): - dist = get_distribution('3.13') + with EnvVars({"HATCH_PYTHON_VARIANT_GIL": "freethreaded"}): + dist = get_distribution("3.13") - assert 'freethreaded' in dist.source + assert "freethreaded" in dist.source def test_incompatible(self, platform): - with EnvVars({'HATCH_PYTHON_VARIANT_GIL': 'freethreaded'}), pytest.raises( - PythonDistributionResolutionError, - match=f"Could not find a default source for name='3.12' system='{platform.name}' arch=", + with ( + EnvVars({"HATCH_PYTHON_VARIANT_GIL": "freethreaded"}), + pytest.raises( + PythonDistributionResolutionError, + match=f"Could not find a default source for name='3.12' system='{platform.name}' arch=", + ), ): - get_distribution('3.12') + get_distribution("3.12") diff --git a/tests/utils/test_auth.py b/tests/utils/test_auth.py index 67e666033..a187e0ace 100644 --- a/tests/utils/test_auth.py +++ b/tests/utils/test_auth.py @@ -4,7 +4,7 @@ def test_pypirc(fs): fs.create_file( - Path.home() / '.pypirc', + Path.home() / ".pypirc", contents="""\ [other] username: guido @@ -18,27 +18,27 @@ def test_pypirc(fs): ) credentials = AuthenticationCredentials( - app=None, cache_dir=Path('/none'), options={}, repo='', repo_config={'url': ''} + app=None, cache_dir=Path("/none"), options={}, repo="", repo_config={"url": ""} ) - assert credentials.username == 'guido' - assert credentials.password == 'sprscrt' + assert credentials.username == "guido" + assert credentials.password == "sprscrt" credentials = AuthenticationCredentials( app=None, - cache_dir=Path('/none'), + cache_dir=Path("/none"), options={}, - repo='other', - repo_config={'url': ''}, + repo="other", + repo_config={"url": ""}, ) - assert credentials.username == 'guido' - assert credentials.password == 'gat' + assert credentials.username == "guido" + assert credentials.password == "gat" credentials = AuthenticationCredentials( app=None, - cache_dir=Path('/none'), + cache_dir=Path("/none"), options={}, - repo='arbritrary', - repo_config={'url': 'https://kaashandel.nl/'}, + repo="arbritrary", + repo_config={"url": "https://kaashandel.nl/"}, ) - assert credentials.username == 'guido' - assert credentials.password == 'gat' + assert credentials.username == "guido" + assert credentials.password == "gat" diff --git a/tests/utils/test_fs.py b/tests/utils/test_fs.py index fbbd5e3ad..9dc5005f2 100644 --- a/tests/utils/test_fs.py +++ b/tests/utils/test_fs.py @@ -15,20 +15,20 @@ def test_resolve_relative_non_existent(self, tmp_path): origin = os.getcwd() os.chdir(tmp_path) try: - expected_representation = os.path.join(tmp_path, 'foo') - assert str(Path('foo').resolve()) == expected_representation - assert str(Path('.', 'foo').resolve()) == expected_representation + expected_representation = os.path.join(tmp_path, "foo") + assert str(Path("foo").resolve()) == expected_representation + assert str(Path(".", "foo").resolve()) == expected_representation finally: os.chdir(origin) def test_ensure_dir_exists(self, tmp_path): - path = Path(tmp_path, 'foo') + path = Path(tmp_path, "foo") path.ensure_dir_exists() assert path.is_dir() def test_ensure_parent_dir_exists(self, tmp_path): - path = Path(tmp_path, 'foo', 'bar') + path = Path(tmp_path, "foo", "bar") path.ensure_parent_dir_exists() assert path.parent.is_dir() @@ -46,15 +46,15 @@ def test_as_cwd_env_vars(self, tmp_path): env_var = str(self).encode().hex().upper() origin = os.getcwd() - with Path(tmp_path).as_cwd(env_vars={env_var: 'foo'}): + with Path(tmp_path).as_cwd(env_vars={env_var: "foo"}): assert os.getcwd() == str(tmp_path) - assert os.environ.get(env_var) == 'foo' + assert os.environ.get(env_var) == "foo" assert os.getcwd() == origin assert env_var not in os.environ def test_remove_file(self, tmp_path): - path = Path(tmp_path, 'foo') + path = Path(tmp_path, "foo") path.touch() assert path.is_file() @@ -62,7 +62,7 @@ def test_remove_file(self, tmp_path): assert not path.exists() def test_remove_directory(self, tmp_path): - path = Path(tmp_path, 'foo') + path = Path(tmp_path, "foo") path.mkdir() assert path.is_dir() @@ -70,14 +70,14 @@ def test_remove_directory(self, tmp_path): assert not path.exists() def test_remove_non_existent(self, tmp_path): - path = Path(tmp_path, 'foo') + path = Path(tmp_path, "foo") assert not path.exists() path.remove() assert not path.exists() def test_temp_hide_file(self, tmp_path): - path = Path(tmp_path, 'foo') + path = Path(tmp_path, "foo") path.touch() with path.temp_hide() as temp_path: @@ -88,7 +88,7 @@ def test_temp_hide_file(self, tmp_path): assert not temp_path.exists() def test_temp_hide_dir(self, tmp_path): - path = Path(tmp_path, 'foo') + path = Path(tmp_path, "foo") path.mkdir() with path.temp_hide() as temp_path: @@ -99,7 +99,7 @@ def test_temp_hide_dir(self, tmp_path): assert not temp_path.exists() def test_temp_hide_non_existent(self, tmp_path): - path = Path(tmp_path, 'foo') + path = Path(tmp_path, "foo") with path.temp_hide() as temp_path: assert not path.exists() diff --git a/tests/utils/test_platform.py b/tests/utils/test_platform.py index 86389f18d..8b7c336a1 100644 --- a/tests/utils/test_platform.py +++ b/tests/utils/test_platform.py @@ -14,24 +14,24 @@ def test_tag(self): assert Platform().windows is True def test_default_shell(self): - assert Platform().default_shell == os.environ.get('COMSPEC', 'cmd') + assert Platform().default_shell == os.environ.get("COMSPEC", "cmd") def test_format_for_subprocess_list(self): - assert Platform().format_for_subprocess(['foo', 'bar'], shell=False) == ['foo', 'bar'] + assert Platform().format_for_subprocess(["foo", "bar"], shell=False) == ["foo", "bar"] def test_format_for_subprocess_list_shell(self): - assert Platform().format_for_subprocess(['foo', 'bar'], shell=True) == ['foo', 'bar'] + assert Platform().format_for_subprocess(["foo", "bar"], shell=True) == ["foo", "bar"] def test_format_for_subprocess_string(self): - assert Platform().format_for_subprocess('foo bar', shell=False) == 'foo bar' + assert Platform().format_for_subprocess("foo bar", shell=False) == "foo bar" def test_format_for_subprocess_string_shell(self): - assert Platform().format_for_subprocess('foo bar', shell=True) == 'foo bar' + assert Platform().format_for_subprocess("foo bar", shell=True) == "foo bar" def test_home(self): platform = Platform() - assert platform.home == platform.home == Path(os.path.expanduser('~')) + assert platform.home == platform.home == Path(os.path.expanduser("~")) def test_populate_default_popen_kwargs_executable(self): platform = Platform() @@ -40,9 +40,9 @@ def test_populate_default_popen_kwargs_executable(self): platform.populate_default_popen_kwargs(kwargs, shell=True) assert not kwargs - kwargs['executable'] = 'foo' + kwargs["executable"] = "foo" platform.populate_default_popen_kwargs(kwargs, shell=True) - assert kwargs['executable'] == 'foo' + assert kwargs["executable"] == "foo" @pytest.mark.requires_macos @@ -51,38 +51,38 @@ def test_tag(self): assert Platform().macos is True def test_default_shell(self): - assert Platform().default_shell == os.environ.get('SHELL', 'bash') + assert Platform().default_shell == os.environ.get("SHELL", "bash") def test_format_for_subprocess_list(self): - assert Platform().format_for_subprocess(['foo', 'bar'], shell=False) == ['foo', 'bar'] + assert Platform().format_for_subprocess(["foo", "bar"], shell=False) == ["foo", "bar"] def test_format_for_subprocess_list_shell(self): - assert Platform().format_for_subprocess(['foo', 'bar'], shell=True) == ['foo', 'bar'] + assert Platform().format_for_subprocess(["foo", "bar"], shell=True) == ["foo", "bar"] def test_format_for_subprocess_string(self): - assert Platform().format_for_subprocess('foo bar', shell=False) == ['foo', 'bar'] + assert Platform().format_for_subprocess("foo bar", shell=False) == ["foo", "bar"] def test_format_for_subprocess_string_shell(self): - assert Platform().format_for_subprocess('foo bar', shell=True) == 'foo bar' + assert Platform().format_for_subprocess("foo bar", shell=True) == "foo bar" def test_home(self): platform = Platform() - assert platform.home == platform.home == Path(os.path.expanduser('~')) + assert platform.home == platform.home == Path(os.path.expanduser("~")) def test_populate_default_popen_kwargs_executable(self, temp_dir): - new_path = f'{os.environ.get("PATH", "")}{os.pathsep}{temp_dir}'.strip(os.pathsep) - executable = temp_dir / 'sh' + new_path = f"{os.environ.get('PATH', '')}{os.pathsep}{temp_dir}".strip(os.pathsep) + executable = temp_dir / "sh" executable.touch() executable.chmod(executable.stat().st_mode | stat.S_IEXEC) kwargs = {} platform = Platform() - with EnvVars({'DYLD_FOO': 'bar', 'PATH': new_path}): + with EnvVars({"DYLD_FOO": "bar", "PATH": new_path}): platform.populate_default_popen_kwargs(kwargs, shell=True) - assert kwargs['executable'] == str(executable) + assert kwargs["executable"] == str(executable) @pytest.mark.requires_linux @@ -91,24 +91,24 @@ def test_tag(self): assert Platform().linux is True def test_default_shell(self): - assert Platform().default_shell == os.environ.get('SHELL', 'bash') + assert Platform().default_shell == os.environ.get("SHELL", "bash") def test_format_for_subprocess_list(self): - assert Platform().format_for_subprocess(['foo', 'bar'], shell=False) == ['foo', 'bar'] + assert Platform().format_for_subprocess(["foo", "bar"], shell=False) == ["foo", "bar"] def test_format_for_subprocess_list_shell(self): - assert Platform().format_for_subprocess(['foo', 'bar'], shell=True) == ['foo', 'bar'] + assert Platform().format_for_subprocess(["foo", "bar"], shell=True) == ["foo", "bar"] def test_format_for_subprocess_string(self): - assert Platform().format_for_subprocess('foo bar', shell=False) == ['foo', 'bar'] + assert Platform().format_for_subprocess("foo bar", shell=False) == ["foo", "bar"] def test_format_for_subprocess_string_shell(self): - assert Platform().format_for_subprocess('foo bar', shell=True) == 'foo bar' + assert Platform().format_for_subprocess("foo bar", shell=True) == "foo bar" def test_home(self): platform = Platform() - assert platform.home == platform.home == Path(os.path.expanduser('~')) + assert platform.home == platform.home == Path(os.path.expanduser("~")) def test_populate_default_popen_kwargs_executable(self): platform = Platform() @@ -117,6 +117,6 @@ def test_populate_default_popen_kwargs_executable(self): platform.populate_default_popen_kwargs(kwargs, shell=True) assert not kwargs - kwargs['executable'] = 'foo' + kwargs["executable"] = "foo" platform.populate_default_popen_kwargs(kwargs, shell=True) - assert kwargs['executable'] == 'foo' + assert kwargs["executable"] == "foo" diff --git a/tests/utils/test_runner.py b/tests/utils/test_runner.py index 44eff4ca6..c8f065c4d 100644 --- a/tests/utils/test_runner.py +++ b/tests/utils/test_runner.py @@ -10,17 +10,17 @@ def test_empty(self): assert parse_matrix_variables(()) == {} def test_single(self): - assert parse_matrix_variables(('py=3.9',)) == {'python': {'3.9'}} + assert parse_matrix_variables(("py=3.9",)) == {"python": {"3.9"}} def test_multiple(self): - assert parse_matrix_variables(('py=3.9', 'version=42')) == {'python': {'3.9'}, 'version': {'42'}} + assert parse_matrix_variables(("py=3.9", "version=42")) == {"python": {"3.9"}, "version": {"42"}} def test_no_values(self): - assert parse_matrix_variables(('py=3.9', 'version')) == {'python': {'3.9'}, 'version': set()} + assert parse_matrix_variables(("py=3.9", "version")) == {"python": {"3.9"}, "version": set()} def test_duplicate(self): with pytest.raises(ValueError): # noqa: PT011 - parse_matrix_variables(('py=3.9', 'py=3.10')) + parse_matrix_variables(("py=3.9", "py=3.10")) class TestSelectEnvironments: @@ -29,72 +29,72 @@ def test_empty(self): def test_no_filters(self): environments = { - 'a': {'python': '3.9', 'feature': 'foo'}, - 'b': {'python': '3.10', 'feature': 'bar'}, - 'c': {'python': '3.11', 'feature': 'baz'}, - 'd': {'python': '3.11', 'feature': 'foo', 'version': '42'}, + "a": {"python": "3.9", "feature": "foo"}, + "b": {"python": "3.10", "feature": "bar"}, + "c": {"python": "3.11", "feature": "baz"}, + "d": {"python": "3.11", "feature": "foo", "version": "42"}, } - assert select_environments(environments, {}, {}) == ['a', 'b', 'c', 'd'] + assert select_environments(environments, {}, {}) == ["a", "b", "c", "d"] def test_include_any(self): environments = { - 'a': {'python': '3.9', 'feature': 'foo'}, - 'b': {'python': '3.10', 'feature': 'bar'}, - 'c': {'python': '3.11', 'feature': 'baz'}, - 'd': {'python': '3.11', 'feature': 'foo', 'version': '42'}, + "a": {"python": "3.9", "feature": "foo"}, + "b": {"python": "3.10", "feature": "bar"}, + "c": {"python": "3.11", "feature": "baz"}, + "d": {"python": "3.11", "feature": "foo", "version": "42"}, } - assert select_environments(environments, {'version': set()}, {}) == ['d'] + assert select_environments(environments, {"version": set()}, {}) == ["d"] def test_include_specific(self): environments = { - 'a': {'python': '3.9', 'feature': 'foo'}, - 'b': {'python': '3.10', 'feature': 'bar'}, - 'c': {'python': '3.11', 'feature': 'baz'}, - 'd': {'python': '3.11', 'feature': 'foo', 'version': '42'}, + "a": {"python": "3.9", "feature": "foo"}, + "b": {"python": "3.10", "feature": "bar"}, + "c": {"python": "3.11", "feature": "baz"}, + "d": {"python": "3.11", "feature": "foo", "version": "42"}, } - assert select_environments(environments, {'python': {'3.11'}}, {}) == ['c', 'd'] + assert select_environments(environments, {"python": {"3.11"}}, {}) == ["c", "d"] def test_include_multiple(self): environments = { - 'a': {'python': '3.9', 'feature': 'foo'}, - 'b': {'python': '3.10', 'feature': 'bar'}, - 'c': {'python': '3.11', 'feature': 'baz'}, - 'd': {'python': '3.11', 'feature': 'foo', 'version': '42'}, + "a": {"python": "3.9", "feature": "foo"}, + "b": {"python": "3.10", "feature": "bar"}, + "c": {"python": "3.11", "feature": "baz"}, + "d": {"python": "3.11", "feature": "foo", "version": "42"}, } - assert select_environments(environments, {'python': {'3.11'}, 'feature': {'baz'}}, {}) == ['c'] + assert select_environments(environments, {"python": {"3.11"}, "feature": {"baz"}}, {}) == ["c"] def test_exclude_any(self): environments = { - 'a': {'python': '3.9', 'feature': 'foo'}, - 'b': {'python': '3.10', 'feature': 'bar'}, - 'c': {'python': '3.11', 'feature': 'baz'}, - 'd': {'python': '3.11', 'feature': 'foo', 'version': '42'}, + "a": {"python": "3.9", "feature": "foo"}, + "b": {"python": "3.10", "feature": "bar"}, + "c": {"python": "3.11", "feature": "baz"}, + "d": {"python": "3.11", "feature": "foo", "version": "42"}, } - assert select_environments(environments, {}, {'version': set()}) == ['a', 'b', 'c'] + assert select_environments(environments, {}, {"version": set()}) == ["a", "b", "c"] def test_exclude_specific(self): environments = { - 'a': {'python': '3.9', 'feature': 'foo'}, - 'b': {'python': '3.10', 'feature': 'bar'}, - 'c': {'python': '3.11', 'feature': 'baz'}, - 'd': {'python': '3.11', 'feature': 'foo', 'version': '42'}, + "a": {"python": "3.9", "feature": "foo"}, + "b": {"python": "3.10", "feature": "bar"}, + "c": {"python": "3.11", "feature": "baz"}, + "d": {"python": "3.11", "feature": "foo", "version": "42"}, } - assert select_environments(environments, {}, {'python': {'3.11'}}) == ['a', 'b'] + assert select_environments(environments, {}, {"python": {"3.11"}}) == ["a", "b"] def test_exclude_multiple(self): environments = { - 'a': {'python': '3.9', 'feature': 'foo'}, - 'b': {'python': '3.10', 'feature': 'bar'}, - 'c': {'python': '3.11', 'feature': 'baz'}, - 'd': {'python': '3.11', 'feature': 'foo', 'version': '42'}, + "a": {"python": "3.9", "feature": "foo"}, + "b": {"python": "3.10", "feature": "bar"}, + "c": {"python": "3.11", "feature": "baz"}, + "d": {"python": "3.11", "feature": "foo", "version": "42"}, } - assert select_environments(environments, {}, {'python': {'3.11'}, 'feature': {'baz'}}) == ['a', 'b'] + assert select_environments(environments, {}, {"python": {"3.11"}, "feature": {"baz"}}) == ["a", "b"] def test_include_and_exclude(self): environments = { - 'a': {'python': '3.9', 'feature': 'foo'}, - 'b': {'python': '3.10', 'feature': 'bar'}, - 'c': {'python': '3.11', 'feature': 'baz'}, - 'd': {'python': '3.11', 'feature': 'foo', 'version': '42'}, + "a": {"python": "3.9", "feature": "foo"}, + "b": {"python": "3.10", "feature": "bar"}, + "c": {"python": "3.11", "feature": "baz"}, + "d": {"python": "3.11", "feature": "foo", "version": "42"}, } - assert select_environments(environments, {'python': {'3.11'}}, {'feature': {'baz'}}) == ['d'] + assert select_environments(environments, {"python": {"3.11"}}, {"feature": {"baz"}}) == ["d"] diff --git a/tests/utils/test_structures.py b/tests/utils/test_structures.py index d8f79dba2..063f2b864 100644 --- a/tests/utils/test_structures.py +++ b/tests/utils/test_structures.py @@ -18,44 +18,44 @@ def test_restoration(self): def test_set(self): env_var = get_random_name() - with EnvVars({env_var: 'foo'}): - assert os.environ.get(env_var) == 'foo' + with EnvVars({env_var: "foo"}): + assert os.environ.get(env_var) == "foo" assert env_var not in os.environ def test_include(self): env_var = get_random_name() - pattern = f'{env_var[:-2]}*' + pattern = f"{env_var[:-2]}*" - with EnvVars({env_var: 'foo'}): + with EnvVars({env_var: "foo"}): num_env_vars = len(os.environ) with EnvVars(include=[get_random_name(), pattern]): assert len(os.environ) == 1 - assert os.environ.get(env_var) == 'foo' + assert os.environ.get(env_var) == "foo" assert len(os.environ) == num_env_vars def test_exclude(self): env_var = get_random_name() - pattern = f'{env_var[:-2]}*' + pattern = f"{env_var[:-2]}*" - with EnvVars({env_var: 'foo'}): + with EnvVars({env_var: "foo"}): with EnvVars(exclude=[get_random_name(), pattern]): assert env_var not in os.environ - assert os.environ.get(env_var) == 'foo' + assert os.environ.get(env_var) == "foo" def test_precedence(self): env_var1 = get_random_name() env_var2 = get_random_name() - pattern = f'{env_var1[:-2]}*' + pattern = f"{env_var1[:-2]}*" - with EnvVars({env_var1: 'foo'}): + with EnvVars({env_var1: "foo"}): num_env_vars = len(os.environ) - with EnvVars({env_var2: 'bar'}, include=[pattern], exclude=[pattern, env_var2]): + with EnvVars({env_var2: "bar"}, include=[pattern], exclude=[pattern, env_var2]): assert len(os.environ) == 1 - assert os.environ.get(env_var2) == 'bar' + assert os.environ.get(env_var2) == "bar" assert len(os.environ) == num_env_vars diff --git a/tests/venv/test_core.py b/tests/venv/test_core.py index 43b80a1fc..3f0e02d5e 100644 --- a/tests/venv/test_core.py +++ b/tests/venv/test_core.py @@ -10,23 +10,23 @@ def test_initialization_does_not_create(temp_dir, platform): - venv_dir = temp_dir / 'venv' + venv_dir = temp_dir / "venv" venv = VirtualEnv(venv_dir, platform) assert not venv.exists() - with pytest.raises(OSError, match=f'Unable to locate executables directory within: {re.escape(str(venv_dir))}'): + with pytest.raises(OSError, match=f"Unable to locate executables directory within: {re.escape(str(venv_dir))}"): _ = venv.executables_directory def test_remove_non_existent_no_error(temp_dir, platform): - venv_dir = temp_dir / 'venv' + venv_dir = temp_dir / "venv" venv = VirtualEnv(venv_dir, platform) venv.remove() def test_creation(temp_dir, platform): - venv_dir = temp_dir / 'venv' + venv_dir = temp_dir / "venv" venv = VirtualEnv(venv_dir, platform) venv.create(sys.executable) @@ -35,124 +35,124 @@ def test_creation(temp_dir, platform): def test_executables_directory(temp_dir, platform): - venv_dir = temp_dir / 'venv' + venv_dir = temp_dir / "venv" venv = VirtualEnv(venv_dir, platform) venv.create(sys.executable) assert venv.executables_directory.is_dir() for entry in venv.executables_directory.iterdir(): - if entry.name.startswith('py'): + if entry.name.startswith("py"): break else: # no cov - msg = 'Unable to locate Python executable' + msg = "Unable to locate Python executable" raise AssertionError(msg) def test_activation(temp_dir, platform): - venv_dir = temp_dir / 'venv' + venv_dir = temp_dir / "venv" venv = VirtualEnv(venv_dir, platform) venv.create(sys.executable) with EnvVars(exclude=VirtualEnv.IGNORED_ENV_VARS): - os.environ['PATH'] = str(temp_dir) - os.environ['VIRTUAL_ENV'] = 'foo' + os.environ["PATH"] = str(temp_dir) + os.environ["VIRTUAL_ENV"] = "foo" for env_var in VirtualEnv.IGNORED_ENV_VARS: - os.environ[env_var] = 'foo' + os.environ[env_var] = "foo" venv.activate() - assert os.environ['PATH'] == f'{venv.executables_directory}{os.pathsep}{temp_dir}' - assert os.environ['VIRTUAL_ENV'] == str(venv_dir) + assert os.environ["PATH"] == f"{venv.executables_directory}{os.pathsep}{temp_dir}" + assert os.environ["VIRTUAL_ENV"] == str(venv_dir) for env_var in VirtualEnv.IGNORED_ENV_VARS: assert env_var not in os.environ venv.deactivate() - assert os.environ['PATH'] == str(temp_dir) - assert os.environ['VIRTUAL_ENV'] == 'foo' + assert os.environ["PATH"] == str(temp_dir) + assert os.environ["VIRTUAL_ENV"] == "foo" for env_var in VirtualEnv.IGNORED_ENV_VARS: - assert os.environ[env_var] == 'foo' + assert os.environ[env_var] == "foo" def test_activation_path_env_var_missing(temp_dir, platform): - venv_dir = temp_dir / 'venv' + venv_dir = temp_dir / "venv" venv = VirtualEnv(venv_dir, platform) venv.create(sys.executable) with EnvVars(exclude=VirtualEnv.IGNORED_ENV_VARS): - os.environ.pop('PATH', None) - os.environ['VIRTUAL_ENV'] = 'foo' + os.environ.pop("PATH", None) + os.environ["VIRTUAL_ENV"] = "foo" for env_var in VirtualEnv.IGNORED_ENV_VARS: - os.environ[env_var] = 'foo' + os.environ[env_var] = "foo" venv.activate() - assert os.environ['PATH'] == f'{venv.executables_directory}{os.pathsep}{os.defpath}' - assert os.environ['VIRTUAL_ENV'] == str(venv_dir) + assert os.environ["PATH"] == f"{venv.executables_directory}{os.pathsep}{os.defpath}" + assert os.environ["VIRTUAL_ENV"] == str(venv_dir) for env_var in VirtualEnv.IGNORED_ENV_VARS: assert env_var not in os.environ venv.deactivate() - assert 'PATH' not in os.environ - assert os.environ['VIRTUAL_ENV'] == 'foo' + assert "PATH" not in os.environ + assert os.environ["VIRTUAL_ENV"] == "foo" for env_var in VirtualEnv.IGNORED_ENV_VARS: - assert os.environ[env_var] == 'foo' + assert os.environ[env_var] == "foo" def test_context_manager(temp_dir, platform, extract_installed_requirements): - venv_dir = temp_dir / 'venv' + venv_dir = temp_dir / "venv" venv = VirtualEnv(venv_dir, platform) venv.create(sys.executable) with EnvVars(exclude=VirtualEnv.IGNORED_ENV_VARS): - os.environ['PATH'] = str(temp_dir) - os.environ['VIRTUAL_ENV'] = 'foo' + os.environ["PATH"] = str(temp_dir) + os.environ["VIRTUAL_ENV"] = "foo" for env_var in VirtualEnv.IGNORED_ENV_VARS: - os.environ[env_var] = 'foo' + os.environ[env_var] = "foo" with venv: - assert os.environ['PATH'] == f'{venv.executables_directory}{os.pathsep}{temp_dir}' - assert os.environ['VIRTUAL_ENV'] == str(venv_dir) + assert os.environ["PATH"] == f"{venv.executables_directory}{os.pathsep}{temp_dir}" + assert os.environ["VIRTUAL_ENV"] == str(venv_dir) for env_var in VirtualEnv.IGNORED_ENV_VARS: assert env_var not in os.environ # Run here while we have cleanup - output = platform.run_command(['pip', 'freeze'], check=True, capture_output=True).stdout.decode('utf-8') + output = platform.run_command(["pip", "freeze"], check=True, capture_output=True).stdout.decode("utf-8") assert not extract_installed_requirements(output.splitlines()) - assert os.environ['PATH'] == str(temp_dir) - assert os.environ['VIRTUAL_ENV'] == 'foo' + assert os.environ["PATH"] == str(temp_dir) + assert os.environ["VIRTUAL_ENV"] == "foo" for env_var in VirtualEnv.IGNORED_ENV_VARS: - assert os.environ[env_var] == 'foo' + assert os.environ[env_var] == "foo" def test_creation_allow_system_packages(temp_dir, platform, extract_installed_requirements): - venv_dir = temp_dir / 'venv' + venv_dir = temp_dir / "venv" venv = VirtualEnv(venv_dir, platform) venv.create(sys.executable, allow_system_packages=True) with venv: - output = platform.run_command(['pip', 'freeze'], check=True, capture_output=True).stdout.decode('utf-8') + output = platform.run_command(["pip", "freeze"], check=True, capture_output=True).stdout.decode("utf-8") assert len(extract_installed_requirements(output.splitlines())) > 0 def test_python_data(temp_dir, platform): - venv_dir = temp_dir / 'venv' + venv_dir = temp_dir / "venv" venv = VirtualEnv(venv_dir, platform) venv.create(sys.executable) with venv: output = platform.run_command( - ['python', '-W', 'ignore', '-'], + ["python", "-W", "ignore", "-"], check=True, capture_output=True, - input=b'import json,sys;print(json.dumps([path for path in sys.path if path]))', - ).stdout.decode('utf-8') + input=b"import json,sys;print(json.dumps([path for path in sys.path if path]))", + ).stdout.decode("utf-8") assert venv.environment is venv.environment assert venv.sys_path is venv.sys_path - assert venv.environment['sys_platform'] == sys.platform + assert venv.environment["sys_platform"] == sys.platform assert venv.sys_path == json.loads(output)