diff --git a/LICENSE.txt b/LICENSE.txt index 1cd6724..5082b9b 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,23 +1,25 @@ -Refactor -~~~~~~~~ +The MIT License (MIT) +===================== -Copyright (c) 2020 Batuhan Taskaya +Copyright © `2022` `Batuhan Taskaya` -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the “Software”), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: -1. In any case, this project should be retain the above copyright notice, - and credits to project github page. -2. The name of the author can be used to endorse or promote products - derived from this software without specific prior written permission. +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. -THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR -IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN -NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED -TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. diff --git a/docs/source/changelog.md b/docs/source/changelog.md index 760f894..7dc1233 100644 --- a/docs/source/changelog.md +++ b/docs/source/changelog.md @@ -1,5 +1,9 @@ # Changelog +## 0.4.2 + +- Fix handling of `--refactor-file`. Reported and contributed by [gerrymanoim](https://github.com/gerrymanoim). + ## 0.4.1 - Preserve indented literal expressions (e.g the first argument of the following call): diff --git a/refactor/__main__.py b/refactor/__main__.py index efdcd60..cb2879e 100644 --- a/refactor/__main__.py +++ b/refactor/__main__.py @@ -1,13 +1,13 @@ import importlib import importlib.util from argparse import ArgumentParser +from itertools import chain from pathlib import Path from typing import Iterable, Type from refactor.core import Rule, Session -from refactor.runner import run_files - -_POSSIBLE_FILES = [Path("refactors.py"), Path(".refactors/__init__.py")] +from refactor.runner import expand_paths, run_files +from refactor.validate_inputs import validate_main_inputs def get_refactors(path: Path) -> Iterable[Type[Rule]]: @@ -41,25 +41,13 @@ def main() -> int: ) options = parser.parse_args() - if options.refactor_file: - refactor_file = options.refactor_dir - if refactor_file.exists(): - raise ValueError( - f"Given --refactor-file '{refactor_file!s}' doesn't exist" - ) - else: - for refactor_file in _POSSIBLE_FILES: - if refactor_file.exists(): - break - else: - raise ValueError( - "Either provide a file using --refactor-file or ensure one of " - "these directories exist: " - + ", ".join(map(str, _POSSIBLE_FILES)) - ) + validate_main_inputs(options) - session = Session(list(get_refactors(refactor_file))) - return run_files(session, options.src, apply=options.dont_apply, workers=1) + session = Session(list(get_refactors(options.refactor_file))) + files = chain.from_iterable( + expand_paths(source_dest) for source_dest in options.src + ) + return run_files(session, files, apply=options.dont_apply, workers=1) if __name__ == "__main__": diff --git a/refactor/validate_inputs.py b/refactor/validate_inputs.py new file mode 100644 index 0000000..f4e8ae8 --- /dev/null +++ b/refactor/validate_inputs.py @@ -0,0 +1,30 @@ +from argparse import Namespace +from pathlib import Path + +_DEFAULT_FILES = [Path("refactors.py"), Path(".refactors/__init__.py")] + + +def validate_main_inputs(options: Namespace) -> None: + """Validates options parsed from `main` runner. + + Note + ---- + In the case where no ``--refactor-file`` is passed and a default file is + matched, ``options.refactor_file`` will be set to that value. + """ + if refactor_file := options.refactor_file: + if not refactor_file.exists(): + raise ValueError( + f"Given --refactor-file '{refactor_file!s}' doesn't exist" + ) + else: + for refactor_file in _DEFAULT_FILES: + if refactor_file.exists(): + options.refactor_file = refactor_file + break + else: + raise ValueError( + "Either provide a file using --refactor-file or ensure one of " + "these directories exist: " + + ", ".join(map(str, _DEFAULT_FILES)) + ) diff --git a/setup.cfg b/setup.cfg index 85d65d9..6a37bd2 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,14 +1,16 @@ [metadata] name = refactor -version = 0.4.1 +version = 0.4.2 description = AST-based fragmental source code refactoring toolkit long_description = file: README.md long_description_content_type = text/markdown url = https://github.com/isidentical/refactor author = isidentical author_email = isidentical@gmail.com +license = MIT license_file = LICENSE.txt classifiers = + License :: OSI Approved :: MIT License Programming Language :: Python :: 3 Programming Language :: Python :: 3 :: Only Programming Language :: Python :: 3.9 diff --git a/tests/test_validation.py b/tests/test_validation.py new file mode 100644 index 0000000..fec10de --- /dev/null +++ b/tests/test_validation.py @@ -0,0 +1,47 @@ +import os +from argparse import Namespace +from contextlib import contextmanager +from pathlib import Path +from tempfile import NamedTemporaryFile, TemporaryDirectory +from typing import Generator + +import pytest + +from refactor.validate_inputs import _DEFAULT_FILES, validate_main_inputs + + +@contextmanager +def add_to_default(p: Path) -> Generator: + try: + _DEFAULT_FILES.append(p) + yield + finally: + _DEFAULT_FILES.pop() + + +def test_valid_inputs(): + with NamedTemporaryFile() as f: + options = Namespace(refactor_file=Path(f.name)) + validate_main_inputs(options) + assert options.refactor_file == Path(f.name) + + with NamedTemporaryFile() as f: + tmp_file = Path(f.name) + options = Namespace(refactor_file=Path(f.name)) + with add_to_default(tmp_file): + validate_main_inputs(options) + assert options.refactor_file == tmp_file + + +@pytest.mark.parametrize( + "invalid_options", + [ + Namespace(refactor_file=None), + Namespace(refactor_file=Path("/some/bad/file/somewhere.py")), + ], +) +def test_invalid_inputs(invalid_options): + with pytest.raises(ValueError): + with TemporaryDirectory() as tmp_dir: + os.chdir(tmp_dir) + validate_main_inputs(invalid_options)