From b49097de472e6c5a54a783933a89ffff8342ce86 Mon Sep 17 00:00:00 2001 From: BJ Dierkes Date: Sat, 22 Jun 2024 02:40:07 -0500 Subject: [PATCH] Type Annotations: utils.fs - Resolves Issue #688 - Related to PR #628 --- CHANGELOG.md | 8 +++++++- cement/utils/fs.py | 44 ++++++++++++++++++++++++++++++-------------- 2 files changed, 37 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a9814ebb..630b098d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,10 @@ Refactoring: - [Issue #671](https://github.com/datafolklabs/cement/issues/671) - [PR #681](https://github.com/datafolklabs/cement/pull/681) - `[dev]` Remove Python 3.5, 3.6, 3.7 Docker Dev Targets +- `[dev]` Added Python 3.13 Dev Target +- `[utils.fs]` Type Annotations: utils.fs + - [Issue #688](https://github.com/datafolklabs/cement/issues/688) + - [PR #628](https://github.com/datafolklabs/cement/pull/628) Misc: @@ -35,7 +39,9 @@ Deprecations: Special Recognitions: -Many thanks to `sigma67` for their contributions in modernizing the packaging system. Cement was started in 2009, and has some lingering technical debt that is now being addressed. Their contribution was a major help in moving off of setuptools and on to PDM and `pyproject.toml`, along with initial implementations of Ruff for a new generation of code compliance. I sincerely appreciate your help! +Many thanks to @sigma67 for their contributions in modernizing the packaging system. Cement was started in 2009, and has some lingering technical debt that is now being addressed. Their contribution was a major help in moving off of setuptools and on to PDM and `pyproject.toml`, along with initial implementations of Ruff for a new generation of code compliance. I sincerely appreciate your help! + +Many thanks to @rednar for their contributions toward adding type annotations in [PR 628](https://github.com/datafolklabs/cement/pull/628). This PR was too large to merge directly, but it serving as a guide to finally begin work toward adding type annotations to Cement. This was a massive effort, and is very helpful to have this work available to guide the effort even if it will not be merged directly. ## 3.0.10 - Feb 28, 2024 diff --git a/cement/utils/fs.py b/cement/utils/fs.py index fa0644d9..e953719f 100644 --- a/cement/utils/fs.py +++ b/cement/utils/fs.py @@ -1,9 +1,16 @@ """Common File System Utilities.""" +# derks@2024-06-22: remove after 3.9 is EOL? +from __future__ import annotations + import os import tempfile import shutil from datetime import datetime +# from typing import Any, Optional, TYPE_CHECKING +from typing import Any, Optional +from typing_extensions import Self +from types import TracebackType class Tmp(object): @@ -11,7 +18,7 @@ class Tmp(object): """ Provides creation and cleanup of a separate temporary directory, and file. - Keyword Arguments: + Keyword Args: cleanup (bool): Whether or not to delete the temporary directory and file on exit (when used with the ``with`` operator). suffix (str): The suffix that the directory and file will end with. @@ -37,7 +44,10 @@ class Tmp(object): """ - def __init__(self, **kwargs): + dir: str + file: str + + def __init__(self, **kwargs: str) -> None: self.cleanup = kwargs.get('cleanup', True) suffix = kwargs.get('suffix', '') prefix = kwargs.get('prefix', 'tmp') @@ -50,7 +60,7 @@ class Tmp(object): prefix=prefix, dir=dir) - def remove(self): + def remove(self) -> None: """ Remove the temporary directory (and file) if it exists, and ``self.cleanup`` is ``True``. @@ -61,20 +71,24 @@ class Tmp(object): if os.path.exists(self.file): os.remove(self.file) - def __enter__(self): + def __enter__(self) -> Self: return self - def __exit__(self, exc_type, exc_value, exc_traceback): + def __exit__(self, + __exc_type: type[BaseException] | None, + __exc_value: BaseException | None, + __exc_traceback: TracebackType | None) -> None: self.remove() -def abspath(path, strip_trailing_slash=True): +def abspath(path: str, strip_trailing_slash: bool = True) -> str: """ Return an absolute path, while also expanding the ``~`` user directory shortcut. Args: path (str): The original path to expand. + Returns: str: The fully expanded, absolute path to the given ``path`` @@ -92,7 +106,7 @@ def abspath(path, strip_trailing_slash=True): return os.path.abspath(os.path.expanduser(path)) -def join(*args, **kwargs): +def join(*args: str, **kwargs: Any) -> str: """ Return a complete, joined path, by first calling ``abspath()`` on the first item to ensure the final path is complete. @@ -117,12 +131,12 @@ def join(*args, **kwargs): return os.path.join(first_path, *paths, **kwargs) -def join_exists(*paths): +def join_exists(*paths: str) -> tuple[str, bool]: """ Wrapper around ``os.path.join()``, ``os.path.abspath()``, and ``os.path.exists()``. - Arguments: + Args: paths (list): List of paths to join, and then return ``True`` if that path exists, or ``False`` if it does not. @@ -134,11 +148,11 @@ def join_exists(*paths): return (path, os.path.exists(path)) -def ensure_dir_exists(path): +def ensure_dir_exists(path: str) -> None: """ Ensure the directory ``path`` exists, and if not create it. - Arguments: + Args: path (str): The filesystem path of a directory. Raises: @@ -156,12 +170,12 @@ def ensure_dir_exists(path): os.makedirs(path) -def ensure_parent_dir_exists(path): +def ensure_parent_dir_exists(path: str) -> None: """ Ensure the parent directory of ``path`` (file, or directory) exists, and if not create it. - Arguments: + Args: path (str): The filesystem path of a file or directory. Returns: None @@ -171,7 +185,7 @@ def ensure_parent_dir_exists(path): return ensure_dir_exists(parent_dir) -def backup(path, suffix='.bak', **kwargs): +def backup(path: str, suffix: str = '.bak', **kwargs: Any) -> Optional[str]: """ Rename a file or directory safely without overwriting an existing backup of the same name. @@ -179,6 +193,8 @@ def backup(path, suffix='.bak', **kwargs): Args: path (str): The path to the file or directory to make a backup of. suffix (str): The suffix to rename files with. + + Keyword Args: timestamp(bool): whether to add a timestamp to the backup suffix timestamp_format(str): Date-Time format in python datetime.datetime notation. default '%Y-%m-%d-%H:%M:%S'