diff --git a/pyproject.toml b/pyproject.toml index 9bad5f4..bbe47c8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -29,9 +29,62 @@ packages = [{ include = 'randfacts' }] [tool.poetry.scripts] randfacts = 'randfacts.randfacts:_cli_entrypoint' +[tool.pytest.ini_options] +asyncio_default_fixture_loop_scope = "function" + [tool.pyright] reportUnusedCallResult = false +[tool.ruff.lint] +preview = true +select = ["ALL"] + +ignore = [ + # complains about tab indentation + "W191", + "D206", + # adds a line break before a class docstring + "D203", + # puts the first line summary of a docstring on a different line than the """ + "D213", + # tries to add a blank line after the last docstring section + "D413", + # yells at you if you use a bool typed function argument + "FBT001", + "FBT002", + # yells at you for using try-except in a for loop + "PERF203", + # allow for the use of Any + "ANN401", + # false positives for overriding methods (i think) + "PLR6301", + # disable too many branches check + "PLR0912", + # copyright at top of file + "CPY", + # complains about random.choice() not being good for cryptography + "S311", +] + + +[tool.ruff.lint.per-file-ignores] +"tests/*" = ["S101", "ANN001", "ANN002", "PLC2701", "ARG002", "PLR2004", "DOC"] +"randfacts/randfacts.py" = ["T201"] +"randfacts/__main__.py" = ["D100"] + +[tool.ruff.lint.pydocstyle] +convention = "google" + +[tool.ruff.format] +quote-style = "single" +indent-style = "tab" +line-ending = "lf" + +[tool.ruff.lint.isort] +combine-as-imports = true +force-wrap-aliases = true +order-by-type = true + [tool.poetry.dependencies] python = "^3.6" diff --git a/randfacts/__init__.py b/randfacts/__init__.py index 7cad44c..f115bc5 100644 --- a/randfacts/__init__.py +++ b/randfacts/__init__.py @@ -5,66 +5,68 @@ execution via the command line. See the examples section for more details. Code Examples: - Example usage of randfacts in code. + Example usage of randfacts in code. - generate a random sfw (safe for work) fact. + generate a random sfw (safe for work) fact. - >>> randfacts.get_fact() + >>> randfacts.get_fact() - generate a random nsfw (not safe for work) fact. + generate a random nsfw (not safe for work) fact. - >>> randfacts.get_fact(only_unsafe=true) + >>> randfacts.get_fact(only_unsafe=true) - generate a random mixed fact (possibility of both sfw and nsfw facts) + generate a random mixed fact (possibility of both sfw and nsfw facts) - >>> randfacts.get_fact(false) - >>> # or - >>> randfacts.get_fact(filter_enabled=False) + >>> randfacts.get_fact(false) + >>> # or + >>> randfacts.get_fact(filter_enabled=False) CLI Examples: - randfacts can be executed via the command line with the following commands: + randfacts can be executed via the command line with the following commands: - Normal execution; only safe facts + Normal execution; only safe facts - $ python3 -m randfacts + $ python3 -m randfacts - The unsafe argument can be supplied to provide only unsafe facts + The unsafe argument can be supplied to provide only unsafe facts - $ python3 -m randfacts --unsafe + $ python3 -m randfacts --unsafe - The mixed argument can be provided to provide both SFW and NSFW facts. + The mixed argument can be provided to provide both SFW and NSFW facts. - $ python3 -m randfacts --mixed + $ python3 -m randfacts --mixed - More help. + More help. - $ python3 -m randfacts --help + $ python3 -m randfacts --help """ import warnings as _warnings from randfacts.randfacts import ( - __version__, - all_facts, - get_fact, - safe_facts, - unsafe_facts, + __version__, + all_facts, + get_fact, + safe_facts, + unsafe_facts, ) __all__ = [ - "all_facts", - "get_fact", - "safe_facts", - "unsafe_facts", - "__version__", + "__version__", + "all_facts", + "get_fact", + "safe_facts", + "unsafe_facts", ] # Deprecated methods -def getFact(filter_enabled: bool = True, only_unsafe: bool = False): - """This method is deprecated. Please use get_fact""" - _warnings.warn( - "getFact is deprecated. Please use get_fact", DeprecationWarning, stacklevel=2 - ) - return get_fact(filter_enabled, only_unsafe) +def getFact(filter_enabled: bool = True, only_unsafe: bool = False) -> str: # noqa: N802 + """This method is deprecated. Please use get_fact.""" + _warnings.warn( + "getFact is deprecated. Please use get_fact", + DeprecationWarning, + stacklevel=2, + ) + return get_fact(filter_enabled, only_unsafe) # noqa: DOC201 diff --git a/randfacts/randfacts.py b/randfacts/randfacts.py index 43beafd..f59dfe8 100644 --- a/randfacts/randfacts.py +++ b/randfacts/randfacts.py @@ -1,92 +1,92 @@ +"""Contains the core functionality of randfacts.""" + import argparse +import contextlib import importlib.metadata -import os -import sys +from pathlib import Path from random import choice -dir_path = os.path.dirname(os.path.realpath(__file__)) +dir_path = Path(__file__).resolve().parent __version__ = "" -try: - __version__: str = importlib.metadata.version("randfacts") -except Exception: - pass +with contextlib.suppress(Exception): + __version__: str = importlib.metadata.version("randfacts") -with open(os.path.join(dir_path, "safe.txt"), encoding="utf-8") as f: - safe_facts = [ - fact.rstrip("\r\n ") for fact in f.readlines() if fact.rstrip("\r\n ") != "" - ] +with (dir_path / "safe.txt").open(encoding="utf-8") as f: + safe_facts = [fact.rstrip("\r\n ") for fact in f if fact.rstrip("\r\n ")] -with open(os.path.join(dir_path, "unsafe.txt"), encoding="utf-8") as f: - unsafe_facts = [ - fact.rstrip("\r\n ") for fact in f.readlines() if fact.rstrip("\r\n ") != "" - ] +with (dir_path / "unsafe.txt").open(encoding="utf-8") as f: + unsafe_facts = [fact.rstrip("\r\n ") for fact in f if fact.rstrip("\r\n ")] all_facts = safe_facts + unsafe_facts def get_fact(filter_enabled: bool = True, only_unsafe: bool = False) -> str: - """This function returns a random fact. - - Parameters - ---------- - filter_enabled : bool - The `filter_enabled` parameter determines if the function will filter - out potentially inappropriate facts. Defaults to True. + """This function returns a random fact. - only_unsafe : bool - The `only_unsafe` parameter determines if the function will only give - unsafe (NSFW) facts. Takes precedence over the `filter_enabled` argument. + Parameters + ---------- + filter_enabled : bool + The `filter_enabled` parameter determines if the function will filter + out potentially inappropriate facts. Defaults to True. - Returns - ------ - str - A random fact. + only_unsafe : bool + The `only_unsafe` parameter determines if the function will only give + unsafe (NSFW) facts. Takes precedence over the `filter_enabled` argument. - """ + Returns: + ------ + str + A random fact. - if only_unsafe: - return choice(unsafe_facts) - if filter_enabled is False: - return choice(all_facts) - return choice(safe_facts) + """ + if only_unsafe: + return choice(unsafe_facts) + if filter_enabled is False: + return choice(all_facts) + return choice(safe_facts) def _cli_entrypoint() -> None: - """Entrypoint for execution via command-line.""" - - parser = argparse.ArgumentParser( - description="Generate random facts from the command-line" - ) - - parser.add_argument( - "-V", - "--version", - action="store_true", - help="Print the package version and exit", - ) - - group = parser.add_mutually_exclusive_group() - group.add_argument( - "-m", "--mixed", action="store_true", help="Include safe and unsafe facts" - ) - - group.add_argument( - "-u", "--unsafe", action="store_true", help="Only include unsafe facts" - ) - - args = parser.parse_args() - - if args.version: - print(__version__) - sys.exit(0) - if args.mixed: - print(get_fact(False)) - elif args.unsafe: - print(get_fact(only_unsafe=True)) - else: - print(get_fact()) + """Entrypoint for execution via command-line.""" + parser = argparse.ArgumentParser( + description="Generate random facts from the command-line", + ) + + parser.add_argument( + "-V", + "--version", + action="store_true", + help="Print the package version and exit", + ) + + group = parser.add_mutually_exclusive_group() + group.add_argument( + "-m", + "--mixed", + action="store_true", + help="Include safe and unsafe facts", + ) + + group.add_argument( + "-u", + "--unsafe", + action="store_true", + help="Only include unsafe facts", + ) + + args = parser.parse_args() + + if args.version: # pyright: ignore[reportAny] + print(__version__) + return + if args.mixed: # pyright: ignore[reportAny] + print(get_fact(filter_enabled=False)) + elif args.unsafe: # pyright: ignore[reportAny] + print(get_fact(only_unsafe=True)) + else: + print(get_fact()) if __name__ == "__main__": - _cli_entrypoint() + _cli_entrypoint() diff --git a/tests/fix_encoding.py b/tests/fix_encoding.py index 7a81c74..393f6a9 100644 --- a/tests/fix_encoding.py +++ b/tests/fix_encoding.py @@ -6,28 +6,28 @@ unsafe_path = parent / "randfacts" / "unsafe.txt" bad_characters = [ - ("‘", "'"), - ("’", "'"), - ("“", '"'), - ("”", '"'), - ("…", "..."), - ("—", "-"), + ("‘", "'"), + ("’", "'"), + ("“", '"'), + ("”", '"'), + ("…", "..."), + ("—", "-"), ] with open(safe_path, "r+", encoding="utf-8") as f: - safe = f.read() + safe = f.read() - for char in bad_characters: - safe = safe.replace(char[0], char[1]) + for char in bad_characters: + safe = safe.replace(char[0], char[1]) - f.seek(0) - f.write(safe) + f.seek(0) + f.write(safe) with open(unsafe_path, "r+", encoding="utf-8") as f: - unsafe = f.read() + unsafe = f.read() - for char in bad_characters: - unsafe = unsafe.replace(char[0], char[1]) + for char in bad_characters: + unsafe = unsafe.replace(char[0], char[1]) - f.seek(0) - f.write(unsafe) + f.seek(0) + f.write(unsafe) diff --git a/tests/test_general.py b/tests/test_general.py index f4c916d..a95c010 100644 --- a/tests/test_general.py +++ b/tests/test_general.py @@ -5,90 +5,94 @@ import pytest sys.path.insert(1, str(pathlib.Path(__file__).parents[1])) -from randfacts import ( # noqa: E402 - getFact, - randfacts, # local randfacts instead of installed version +from randfacts import ( + getFact, + randfacts, # local randfacts instead of installed version ) -def test_get_fact(): - assert isinstance(randfacts.get_fact(), str), "get_fact() must return a string" +def test_get_fact() -> None: + assert isinstance(randfacts.get_fact(), str), "get_fact() must return a string" -def test_getFact_deprecated(): - with pytest.deprecated_call(): - _ = getFact() +def test_getFact_deprecated() -> None: + with pytest.deprecated_call(): + _ = getFact() -def test_all_facts_list(): - assert isinstance(randfacts.all_facts, list), "all_facts must be a list" +def test_all_facts_list() -> None: + assert isinstance(randfacts.all_facts, list), "all_facts must be a list" -def test_safe_facts_list(): - assert isinstance(randfacts.safe_facts, list), "safe_facts must be a list" +def test_safe_facts_list() -> None: + assert isinstance(randfacts.safe_facts, list), "safe_facts must be a list" -def test_unsafe_facts_list(): - assert isinstance(randfacts.unsafe_facts, list), "unsafe_facts must be a list" +def test_unsafe_facts_list() -> None: + assert isinstance(randfacts.unsafe_facts, list), "unsafe_facts must be a list" -def test_cli_no_args(): - child = subprocess.Popen(["python3", "-m", "randfacts"], stdout=subprocess.DEVNULL) - child.communicate() - assert child.returncode == 0, "`python3 -m randfacts` must return with exit code 0" +def test_cli_no_args() -> None: + child = subprocess.Popen(["python3", "-m", "randfacts"], stdout=subprocess.DEVNULL) + child.communicate() + assert child.returncode == 0, "`python3 -m randfacts` must return with exit code 0" -def test_cli_unsafe_args(): - child = subprocess.Popen( - ["python3", "-m", "randfacts", "--unsafe"], stdout=subprocess.DEVNULL - ) - child.communicate() - assert ( - child.returncode == 0 - ), "`python3 -m randfacts --unsafe` must return with exit code 0" +def test_cli_unsafe_args() -> None: + child = subprocess.Popen( + ["python3", "-m", "randfacts", "--unsafe"], + stdout=subprocess.DEVNULL, + ) + child.communicate() + assert ( + child.returncode == 0 + ), "`python3 -m randfacts --unsafe` must return with exit code 0" -def test_cli_mixed_args(): - child = subprocess.Popen( - ["python3", "-m", "randfacts", "--mixed"], stdout=subprocess.DEVNULL - ) - child.communicate() - assert ( - child.returncode == 0 - ), "`python3 -m randfacts --mixed` must return with exit code 0" +def test_cli_mixed_args() -> None: + child = subprocess.Popen( + ["python3", "-m", "randfacts", "--mixed"], + stdout=subprocess.DEVNULL, + ) + child.communicate() + assert ( + child.returncode == 0 + ), "`python3 -m randfacts --mixed` must return with exit code 0" -def test_cli_version(): - child = subprocess.Popen( - ["python3", "-m", "randfacts", "--version"], stdout=subprocess.PIPE, text=True - ) - output, _ = child.communicate() - assert ( - output.strip() == randfacts.__version__ - ), f"`python3 -m randfacts --version` must return {randfacts.__version__}" +def test_cli_version() -> None: + child = subprocess.Popen( + ["python3", "-m", "randfacts", "--version"], + stdout=subprocess.PIPE, + text=True, + ) + output, _ = child.communicate() + assert ( + output.strip() == randfacts.__version__ + ), f"`python3 -m randfacts --version` must return {randfacts.__version__}" -def test_main_entrypoint(): - # Path to the module or script you want to test - script_path = ( - pathlib.Path(__file__).resolve().parents[1] / "randfacts" / "randfacts.py" - ) +def test_main_entrypoint() -> None: + # Path to the module or script you want to test + script_path = ( + pathlib.Path(__file__).resolve().parents[1] / "randfacts" / "randfacts.py" + ) - # Run the script as a subprocess - result = subprocess.run( - ["python", str(script_path)], - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - text=True, - ) + # Run the script as a subprocess + result = subprocess.run( + ["python", str(script_path)], + capture_output=True, + text=True, + check=False, + ) - # Assert the subprocess exits successfully - assert result.returncode == 0, f"Script failed with stderr: {result.stderr}" + # Assert the subprocess exits successfully + assert result.returncode == 0, f"Script failed with stderr: {result.stderr}" @pytest.mark.parametrize("bad_char", ["‘", "’", "“", "”", "…", "—"]) -def test_invalid_characters(bad_char: str): - for index, fact in enumerate(randfacts.all_facts): - assert ( - bad_char not in fact - ), f"Bad character '{bad_char}' found in fact at index {index}" +def test_invalid_characters(bad_char: str) -> None: + for index, fact in enumerate(randfacts.all_facts): + assert ( + bad_char not in fact + ), f"Bad character '{bad_char}' found in fact at index {index}"