Skip to content

Commit

Permalink
Better error handling in CLI
Browse files Browse the repository at this point in the history
  • Loading branch information
barseghyanartur committed Jun 22, 2023
1 parent 61781dc commit 847c3a9
Show file tree
Hide file tree
Showing 8 changed files with 156 additions and 107 deletions.
8 changes: 8 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,16 @@ are used for versioning (schema follows below):
0.3.4 to 0.4).
- All backwards incompatible changes are mentioned in this document.

0.16.1
------
2023-06-23

- Better error handling in CLI.

0.16
----
2023-06-21

.. note::

This release is dedicated to my beloved son - Tigran, who turned 11!
Expand Down
16 changes: 14 additions & 2 deletions docs/cli.rst
Original file line number Diff line number Diff line change
@@ -1,14 +1,26 @@
CLI
===
.. External references
.. _pipx: https://pypa.github.io/pipx/

It's possible to generate files from CLI.

.. note::

For using CLI you should install all common dependencies:
For using CLI you should install all common dependencies.

Install using `pipx`_ (recommended):

.. code-block:: sh
pipx install faker-file[common]
Install using ``pip``.

.. code-block:: sh
pip install faker-file[common]
pip install faker-file[common] --user
List available provider options
-------------------------------
Expand Down
6 changes: 4 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

from setuptools import find_packages, setup

version = "0.16"
version = "0.16.1"

try:
readme = open(os.path.join(os.path.dirname(__file__), "README.rst")).read()
Expand Down Expand Up @@ -127,7 +127,9 @@
url="https://github.com/barseghyanartur/faker-file/",
package_dir={"": "src"},
packages=find_packages(where="./src"),
entry_points={"console_scripts": ["faker-file = faker_file.cli:main"]},
entry_points={
"console_scripts": ["faker-file = faker_file.cli.command:main"]
},
license="MIT",
python_requires=">=3.7",
install_requires=install_requires,
Expand Down
2 changes: 1 addition & 1 deletion src/faker_file/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
__title__ = "faker_file"
__version__ = "0.16"
__version__ = "0.16.1"
__author__ = "Artur Barseghyan <artur.barseghyan@gmail.com>"
__copyright__ = "2022-2023 Artur Barseghyan"
__license__ = "MIT"
Empty file added src/faker_file/cli/__init__.py
Empty file.
91 changes: 91 additions & 0 deletions src/faker_file/cli/command.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import argparse
import sys
import typing

from .. import __version__

__author__ = "Artur Barseghyan <artur.barseghyan@gmail.com>"
__copyright__ = "2023 Artur Barseghyan"
__license__ = "MIT"
__all__ = ("main",)


def main():
try:
from .helpers import (
PROVIDERS,
generate_completion_file,
generate_file,
get_method_kwargs,
is_optional_type,
)
except ImportError:
print("You need to pip install faker-file[common] to use the CLI")
sys.exit(1)

parser = argparse.ArgumentParser(
description="CLI for the faker-file package."
)
subparsers = parser.add_subparsers(
dest="command", help="Available file providers."
)

# Add generate-completion subparser
__generate_completion_subparser = subparsers.add_parser(
"generate-completion",
help="Generate bash completion file.",
)

# Add version subparser
__version_subparser = subparsers.add_parser(
"version",
help="Print version.",
)

for method_name, provider in PROVIDERS.items():
subparser = subparsers.add_parser(
method_name,
help=f"Generate a {method_name.split('_file')[0]} file.",
)
method_kwargs, annotations = get_method_kwargs(provider, method_name)
for arg, default in method_kwargs.items():
arg_type = annotations[arg]
arg_kwargs = {
"default": default,
"help": f"{arg} (default: {default})",
"type": (
arg_type.__args__[0]
if isinstance(arg_type, typing._GenericAlias)
and is_optional_type(arg_type)
else arg_type
),
}

subparser.add_argument(f"--{arg}", **arg_kwargs)

# Add the optional num_files argument
subparser.add_argument(
"--nb_files",
default=1,
type=int,
help="number of files to generate (default: 1)",
)

args = parser.parse_args()

if args.command == "generate-completion":
generate_completion_file()
elif args.command == "version":
print(__version__)
elif args.command:
kwargs = {k: v for k, v in vars(args).items() if k not in ("command",)}
for counter in range(args.nb_files):
output_file = generate_file(args.command, **kwargs)
print(
f"Generated {args.command} file "
f"({counter+1} of {args.nb_files}): "
f"{output_file.data['filename']}"
)
else:
parser.print_help()
sys.exit(1)
126 changes: 27 additions & 99 deletions src/faker_file/cli.py → src/faker_file/cli/helpers.py
Original file line number Diff line number Diff line change
@@ -1,49 +1,46 @@
import argparse
import inspect
import os
import sys
import typing
from copy import deepcopy
from typing import Any, Dict, Tuple, Type

from faker import Faker

from . import __version__
from .base import FileMixin, StringValue
from .providers.bin_file import BinFileProvider
from .providers.csv_file import CsvFileProvider
from .providers.docx_file import DocxFileProvider
from .providers.eml_file import EmlFileProvider
from .providers.epub_file import EpubFileProvider
from .providers.generic_file import GenericFileProvider
from .providers.ico_file import IcoFileProvider
from .providers.jpeg_file import JpegFileProvider
from .providers.mp3_file import Mp3FileProvider
from .providers.odp_file import OdpFileProvider
from .providers.ods_file import OdsFileProvider
from .providers.odt_file import OdtFileProvider
from .providers.pdf_file import PdfFileProvider
from .providers.png_file import PngFileProvider
from .providers.pptx_file import PptxFileProvider
from .providers.rtf_file import RtfFileProvider
from .providers.svg_file import SvgFileProvider
from .providers.tar_file import TarFileProvider
from .providers.txt_file import TxtFileProvider
from .providers.webp_file import WebpFileProvider
from .providers.xlsx_file import XlsxFileProvider
from .providers.xml_file import XmlFileProvider
from .providers.zip_file import ZipFileProvider
from ..base import FileMixin, StringValue
from ..providers.bin_file import BinFileProvider
from ..providers.csv_file import CsvFileProvider
from ..providers.docx_file import DocxFileProvider
from ..providers.eml_file import EmlFileProvider
from ..providers.epub_file import EpubFileProvider
from ..providers.generic_file import GenericFileProvider
from ..providers.ico_file import IcoFileProvider
from ..providers.jpeg_file import JpegFileProvider
from ..providers.mp3_file import Mp3FileProvider
from ..providers.odp_file import OdpFileProvider
from ..providers.ods_file import OdsFileProvider
from ..providers.odt_file import OdtFileProvider
from ..providers.pdf_file import PdfFileProvider
from ..providers.png_file import PngFileProvider
from ..providers.pptx_file import PptxFileProvider
from ..providers.rtf_file import RtfFileProvider
from ..providers.svg_file import SvgFileProvider
from ..providers.tar_file import TarFileProvider
from ..providers.txt_file import TxtFileProvider
from ..providers.webp_file import WebpFileProvider
from ..providers.xlsx_file import XlsxFileProvider
from ..providers.xml_file import XmlFileProvider
from ..providers.zip_file import ZipFileProvider

__author__ = "Artur Barseghyan <artur.barseghyan@gmail.com>"
__copyright__ = "2023 Artur Barseghyan"
__license__ = "MIT"
__all__ = [
__all__ = (
"generate_completion_file",
"generate_file",
"get_method_kwargs",
"is_optional_type",
"main",
]
"PROVIDERS",
)

KWARGS_DROP = {
"self", # Drop as irrelevant
Expand Down Expand Up @@ -212,72 +209,3 @@ def generate_completion_file():
f.write(completion_script)

print(f"Generated bash completion file: {file_path}")


def main():
parser = argparse.ArgumentParser(
description="CLI for the faker-file package."
)
subparsers = parser.add_subparsers(
dest="command", help="Available file providers."
)

# Add generate-completion subparser
__generate_completion_subparser = subparsers.add_parser(
"generate-completion",
help="Generate bash completion file.",
)

# Add version subparser
__version_subparser = subparsers.add_parser(
"version",
help="Print version.",
)

for method_name, provider in PROVIDERS.items():
subparser = subparsers.add_parser(
method_name,
help=f"Generate a {method_name.split('_file')[0]} file.",
)
method_kwargs, annotations = get_method_kwargs(provider, method_name)
for arg, default in method_kwargs.items():
arg_type = annotations[arg]
arg_kwargs = {
"default": default,
"help": f"{arg} (default: {default})",
"type": (
arg_type.__args__[0]
if isinstance(arg_type, typing._GenericAlias)
and is_optional_type(arg_type)
else arg_type
),
}

subparser.add_argument(f"--{arg}", **arg_kwargs)

# Add the optional num_files argument
subparser.add_argument(
"--nb_files",
default=1,
type=int,
help="number of files to generate (default: 1)",
)

args = parser.parse_args()

if args.command == "generate-completion":
generate_completion_file()
elif args.command == "version":
print(__version__)
elif args.command:
kwargs = {k: v for k, v in vars(args).items() if k not in ("command",)}
for counter in range(args.nb_files):
output_file = generate_file(args.command, **kwargs)
print(
f"Generated {args.command} file "
f"({counter+1} of {args.nb_files}): "
f"{output_file.data['filename']}"
)
else:
parser.print_help()
sys.exit(1)
14 changes: 11 additions & 3 deletions src/faker_file/tests/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@
import re
import subprocess
import unittest
from importlib import import_module, reload

from parametrize import parametrize

from ..cli.command import main

__author__ = "Artur Barseghyan <artur.barseghyan@gmail.com>"
__copyright__ = "2022-2023 Artur Barseghyan"
__license__ = "MIT"
Expand All @@ -28,9 +31,6 @@ def convert_value_to_cli_arg(value) -> str:
class TestCLI(unittest.TestCase):
"""CLI tests."""

def setUp(self):
"""Set up."""

@parametrize(
"method_name, kwargs",
[
Expand Down Expand Up @@ -265,3 +265,11 @@ def test_cli_version(self: "TestCLI") -> None:
cmd = ["faker-file", "version"]
res = subprocess.check_output(cmd).strip()
self.assertTrue(VERSION_PATTERN.match(res.decode()))

def test_broken_imports(self: "TestCLI") -> None:
"""Test broken imports."""
_module = import_module("faker_file.cli.helpers")
del _module.__dict__["PROVIDERS"]
with self.assertRaises(SystemExit):
main()
reload(_module)

0 comments on commit 847c3a9

Please sign in to comment.