1
0
mirror of https://github.com/kellyjonbrazil/jc.git synced 2025-07-13 01:20:24 +02:00

refactor to use get_parser()

This commit is contained in:
Kelly Brazil
2024-01-06 18:23:46 -08:00
parent 973c535c72
commit 8e9ff7fa9f
7 changed files with 90 additions and 63 deletions

View File

@ -1,6 +1,6 @@
jc changelog jc changelog
20240103 v1.24.1 20240106 v1.24.1
- Add `kv-dup` parser for Key/Value files with duplicate keys - Add `kv-dup` parser for Key/Value files with duplicate keys
- TODO: Add `path-list` string parser to parse path list strings found in env variables - TODO: Add `path-list` string parser to parse path list strings found in env variables
- Add `--slurp` functionality to wrap output from multiple lines into a single array. - Add `--slurp` functionality to wrap output from multiple lines into a single array.
@ -14,6 +14,7 @@ jc changelog
- Add snap package build scripts - Add snap package build scripts
- Refactor parser aliases for `kv`, `pkg_index_deb`, `lsb_release`, and `os-release` - Refactor parser aliases for `kv`, `pkg_index_deb`, `lsb_release`, and `os-release`
- Add `line_slice` function to `utils.py` - Add `line_slice` function to `utils.py`
- Add `get_parser` function to `lib.py`
- Update copyright date - Update copyright date
20231216 v1.24.0 20231216 v1.24.0

View File

@ -23,10 +23,11 @@ jc - JSON Convert lib module
### get\_parser ### get\_parser
```python ```python
def get_parser(parser_mod_name) -> ModuleType def get_parser(parser_mod_name: Union[str, ModuleType]) -> ModuleType
``` ```
Return the parser module object Return the parser module object and check that the module is a valid
parser module.
Parameters: Parameters:
@ -81,15 +82,25 @@ To get a list of available parser module names, use `parser_mod_list()`.
Alternatively, a parser module object can be supplied: Alternatively, a parser module object can be supplied:
>>> import jc >>> import jc
>>> import jc.parsers.date as jc_date >>> jc_date = jc.get_parser('date')
>>> date_obj = jc.parse(jc_date, 'Tue Jan 18 10:23:07 PST 2022') >>> date_obj = jc.parse(jc_date, 'Tue Jan 18 10:23:07 PST 2022')
>>> print(f'The year is: {date_obj["year"]}') >>> print(f'The year is: {date_obj["year"]}')
The year is: 2022 The year is: 2022
You can also use the lower-level parser modules directly: You can also use the parser modules directly via `get_parser()`:
>>> import jc
>>> jc_date = jc.get_parser('date')
>>> date_obj = jc_date.parse('Tue Jan 18 10:23:07 PST 2022')
>>> print(f'The year is: {date_obj["year"]}')
The year is: 2022
Finally, you can access the low-level parser modules manually:
>>> import jc.parsers.date >>> import jc.parsers.date
>>> jc.parsers.date.parse('Tue Jan 18 10:23:07 PST 2022') >>> date_obj = jc.parsers.date.parse('Tue Jan 18 10:23:07 PST 2022')
>>> print(f'The year is: {date_obj["year"]}')
The year is: 2022
Though, accessing plugin parsers directly is a bit more cumbersome, so Though, accessing plugin parsers directly is a bit more cumbersome, so
this higher-level API is recommended. Here is how you can access plugin this higher-level API is recommended. Here is how you can access plugin
@ -112,7 +123,7 @@ Parameters:
variants of the module name. variants of the module name.
A Module object can also be passed A Module object can also be passed
directly or via _get_parser() directly or via get_parser()
data: (string or data to parse (string or bytes for data: (string or data to parse (string or bytes for
bytes or standard parsers, iterable of bytes or standard parsers, iterable of

View File

@ -237,9 +237,11 @@ Ensure input data is a string. Raises `TypeError` if not.
### line\_slice ### line\_slice
```python ```python
def line_slice(data: Union[str, Iterable], def line_slice(
data: Union[str, Iterable[str], TextIO, bytes, None],
slice_start: Optional[int] = None, slice_start: Optional[int] = None,
slice_end: Optional[int] = None) -> Union[str, Iterable] slice_end: Optional[int] = None
) -> Union[str, Iterable[str], TextIO, bytes, None]
``` ```
Slice input data by lines - lazily, if possible. Slice input data by lines - lazily, if possible.

View File

@ -13,7 +13,7 @@ import subprocess
from typing import List, Dict, Iterable, Union, Optional, TextIO from typing import List, Dict, Iterable, Union, Optional, TextIO
from types import ModuleType from types import ModuleType
from .lib import ( from .lib import (
__version__, parser_info, all_parser_info, parsers, _get_parser, _parser_is_streaming, __version__, parser_info, all_parser_info, parsers, get_parser, _parser_is_streaming,
parser_mod_list, standard_parser_mod_list, plugin_parser_mod_list, streaming_parser_mod_list, parser_mod_list, standard_parser_mod_list, plugin_parser_mod_list, streaming_parser_mod_list,
slurpable_parser_mod_list, _parser_is_slurpable slurpable_parser_mod_list, _parser_is_slurpable
) )
@ -584,7 +584,7 @@ class JcCli():
def set_parser_module_and_parser_name(self) -> None: def set_parser_module_and_parser_name(self) -> None:
if self.magic_found_parser: if self.magic_found_parser:
self.parser_module = _get_parser(self.magic_found_parser) self.parser_module = get_parser(self.magic_found_parser)
self.parser_name = self.parser_shortname(self.magic_found_parser) self.parser_name = self.parser_shortname(self.magic_found_parser)
else: else:
@ -593,7 +593,7 @@ class JcCli():
self.parser_name = self.parser_shortname(arg) self.parser_name = self.parser_shortname(arg)
if self.parser_name in parsers: if self.parser_name in parsers:
self.parser_module = _get_parser(arg) self.parser_module = get_parser(arg)
found = True found = True
break break

View File

@ -7,6 +7,7 @@ from typing import List, Iterable, Optional, Union, Iterator
from types import ModuleType from types import ModuleType
from .jc_types import ParserInfoType, JSONDictType from .jc_types import ParserInfoType, JSONDictType
from jc import appdirs from jc import appdirs
from jc import utils
__version__ = '1.24.1' __version__ = '1.24.1'
@ -242,6 +243,9 @@ def _is_valid_parser_plugin(name: str, local_parsers_dir: str) -> bool:
if hasattr(plugin, 'info') and hasattr(plugin, 'parse'): if hasattr(plugin, 'info') and hasattr(plugin, 'parse'):
del plugin del plugin
return True return True
else:
utils.warning_message([f'Not installing invalid parser plugin "{parser_mod_name}" at {local_parsers_dir}'])
return False
except Exception: except Exception:
return False return False
return False return False
@ -270,9 +274,10 @@ def _parser_argument(parser_mod_name: str) -> str:
parser = _modname_to_cliname(parser_mod_name) parser = _modname_to_cliname(parser_mod_name)
return f'--{parser}' return f'--{parser}'
def get_parser(parser_mod_name) -> ModuleType: def get_parser(parser_mod_name: Union[str, ModuleType]) -> ModuleType:
""" """
Return the parser module object Return the parser module object and check that the module is a valid
parser module.
Parameters: Parameters:
@ -283,13 +288,18 @@ def get_parser(parser_mod_name) -> ModuleType:
Returns: Returns:
Parser: the parser module object Parser: the parser module object
""" """
if isinstance(parser_mod_name, ModuleType): if isinstance(parser_mod_name, ModuleType):
jc_parser = parser_mod_name jc_parser = parser_mod_name
else: else:
try:
jc_parser = _get_parser(parser_mod_name) jc_parser = _get_parser(parser_mod_name)
except ModuleNotFoundError:
raise ModuleNotFoundError(f'"{parser_mod_name}" is not found or is not a valid parser module.')
if not hasattr(jc_parser, 'info') or not hasattr(jc_parser, 'parse'):
raise ModuleNotFoundError(f'"{jc_parser}" is not a valid parser module.')
return jc_parser return jc_parser
def _get_parser(parser_mod_name: str) -> ModuleType: def _get_parser(parser_mod_name: str) -> ModuleType:
@ -382,15 +392,25 @@ def parse(
Alternatively, a parser module object can be supplied: Alternatively, a parser module object can be supplied:
>>> import jc >>> import jc
>>> import jc.parsers.date as jc_date >>> jc_date = jc.get_parser('date')
>>> date_obj = jc.parse(jc_date, 'Tue Jan 18 10:23:07 PST 2022') >>> date_obj = jc.parse(jc_date, 'Tue Jan 18 10:23:07 PST 2022')
>>> print(f'The year is: {date_obj["year"]}') >>> print(f'The year is: {date_obj["year"]}')
The year is: 2022 The year is: 2022
You can also use the lower-level parser modules directly: You can also use the parser modules directly via `get_parser()`:
>>> import jc
>>> jc_date = jc.get_parser('date')
>>> date_obj = jc_date.parse('Tue Jan 18 10:23:07 PST 2022')
>>> print(f'The year is: {date_obj["year"]}')
The year is: 2022
Finally, you can access the low-level parser modules manually:
>>> import jc.parsers.date >>> import jc.parsers.date
>>> jc.parsers.date.parse('Tue Jan 18 10:23:07 PST 2022') >>> date_obj = jc.parsers.date.parse('Tue Jan 18 10:23:07 PST 2022')
>>> print(f'The year is: {date_obj["year"]}')
The year is: 2022
Though, accessing plugin parsers directly is a bit more cumbersome, so Though, accessing plugin parsers directly is a bit more cumbersome, so
this higher-level API is recommended. Here is how you can access plugin this higher-level API is recommended. Here is how you can access plugin
@ -413,7 +433,7 @@ def parse(
variants of the module name. variants of the module name.
A Module object can also be passed A Module object can also be passed
directly or via _get_parser() directly or via get_parser()
data: (string or data to parse (string or bytes for data: (string or data to parse (string or bytes for
bytes or standard parsers, iterable of bytes or standard parsers, iterable of
@ -451,7 +471,7 @@ def parser_mod_list(
"""Returns a list of all available parser module names.""" """Returns a list of all available parser module names."""
plist: List[str] = [] plist: List[str] = []
for p in parsers: for p in parsers:
parser = _get_parser(p) parser = get_parser(p)
if not show_hidden and _parser_is_hidden(parser): if not show_hidden and _parser_is_hidden(parser):
continue continue
@ -473,7 +493,7 @@ def plugin_parser_mod_list(
""" """
plist: List[str] = [] plist: List[str] = []
for p in local_parsers: for p in local_parsers:
parser = _get_parser(p) parser = get_parser(p)
if not show_hidden and _parser_is_hidden(parser): if not show_hidden and _parser_is_hidden(parser):
continue continue
@ -496,7 +516,7 @@ def standard_parser_mod_list(
""" """
plist: List[str] = [] plist: List[str] = []
for p in parsers: for p in parsers:
parser = _get_parser(p) parser = get_parser(p)
if not _parser_is_streaming(parser): if not _parser_is_streaming(parser):
@ -520,7 +540,7 @@ def streaming_parser_mod_list(
""" """
plist: List[str] = [] plist: List[str] = []
for p in parsers: for p in parsers:
parser = _get_parser(p) parser = get_parser(p)
if _parser_is_streaming(parser): if _parser_is_streaming(parser):
@ -544,7 +564,7 @@ def slurpable_parser_mod_list(
""" """
plist: List[str] = [] plist: List[str] = []
for p in parsers: for p in parsers:
parser = _get_parser(p) parser = get_parser(p)
if _parser_is_slurpable(parser): if _parser_is_slurpable(parser):
@ -575,17 +595,10 @@ def parser_info(
documentation: (boolean) include parser docstring if True documentation: (boolean) include parser docstring if True
""" """
if isinstance(parser_mod_name, ModuleType): parser_mod = get_parser(parser_mod_name)
parser_mod = parser_mod_name
parser_mod_name = parser_mod.__name__.split('.')[-1] parser_mod_name = parser_mod.__name__.split('.')[-1]
else:
# ensure parser_mod_name is a true module name and not a cli name
parser_mod_name = _cliname_to_modname(parser_mod_name)
parser_mod = _get_parser(parser_mod_name)
info_dict: ParserInfoType = {} info_dict: ParserInfoType = {}
if hasattr(parser_mod, 'info'):
info_dict['name'] = parser_mod_name info_dict['name'] = parser_mod_name
info_dict['argument'] = _parser_argument(parser_mod_name) info_dict['argument'] = _parser_argument(parser_mod_name)
parser_entry = vars(parser_mod.info) parser_entry = vars(parser_mod.info)
@ -625,7 +638,7 @@ def all_parser_info(
""" """
plist: List[str] = [] plist: List[str] = []
for p in parsers: for p in parsers:
parser = _get_parser(p) parser = get_parser(p)
if not show_hidden and _parser_is_hidden(parser): if not show_hidden and _parser_is_hidden(parser):
continue continue
@ -633,7 +646,7 @@ def all_parser_info(
if not show_deprecated and _parser_is_deprecated(parser): if not show_deprecated and _parser_is_deprecated(parser):
continue continue
plist.append(_cliname_to_modname(p)) plist.append(p)
p_info_list: List[ParserInfoType] = [parser_info(p, documentation=documentation) for p in plist] p_info_list: List[ParserInfoType] = [parser_info(p, documentation=documentation) for p in plist]
@ -647,9 +660,5 @@ def get_help(parser_mod_name: Union[str, ModuleType]) -> None:
**--argument-name** variants of the module name string as well as a **--argument-name** variants of the module name string as well as a
parser module object. parser module object.
""" """
if isinstance(parser_mod_name, ModuleType): jc_parser = get_parser(parser_mod_name)
jc_parser = parser_mod_name
else:
jc_parser = _get_parser(parser_mod_name)
help(jc_parser) help(jc_parser)

View File

@ -1,4 +1,4 @@
.TH jc 1 2024-01-05 1.24.1 "JSON Convert" .TH jc 1 2024-01-06 1.24.1 "JSON Convert"
.SH NAME .SH NAME
\fBjc\fP \- JSON Convert JSONifies the output of many CLI tools, file-types, \fBjc\fP \- JSON Convert JSONifies the output of many CLI tools, file-types,
and strings and strings

View File

@ -78,7 +78,7 @@ class MyTests(unittest.TestCase):
def test_lib_all_parser_info_show_deprecated(self): def test_lib_all_parser_info_show_deprecated(self):
# save old state # save old state
old_parsers = deepcopy(jc.lib.parsers) old_parsers = deepcopy(jc.lib.parsers)
old_get_parser = deepcopy(jc.lib._get_parser) old_get_parser = deepcopy(jc.lib.get_parser)
# mock data # mock data
class mock_parser_info: class mock_parser_info:
@ -92,21 +92,23 @@ class MyTests(unittest.TestCase):
class mock_parser: class mock_parser:
info = mock_parser_info info = mock_parser_info
def parse():
pass
jc.lib.parsers = ['deprecated'] jc.lib.parsers = ['deprecated']
jc.lib._get_parser = lambda x: mock_parser # type: ignore jc.lib.get_parser = lambda x: mock_parser # type: ignore
result = jc.lib.all_parser_info(show_deprecated=True) result = jc.lib.all_parser_info(show_deprecated=True)
# reset # reset
jc.lib.parsers = old_parsers jc.lib.parsers = old_parsers
jc.lib._get_parser = old_get_parser jc.lib.get_parser = old_get_parser
self.assertEqual(len(result), 1) self.assertEqual(len(result), 1)
def test_lib_all_parser_info_show_hidden(self): def test_lib_all_parser_info_show_hidden(self):
# save old state # save old state
old_parsers = deepcopy(jc.lib.parsers) old_parsers = deepcopy(jc.lib.parsers)
old_get_parser = deepcopy(jc.lib._get_parser) old_get_parser = deepcopy(jc.lib.get_parser)
# mock data # mock data
class mock_parser_info: class mock_parser_info:
@ -120,14 +122,16 @@ class MyTests(unittest.TestCase):
class mock_parser: class mock_parser:
info = mock_parser_info info = mock_parser_info
def parse():
pass
jc.lib.parsers = ['deprecated'] jc.lib.parsers = ['deprecated']
jc.lib._get_parser = lambda x: mock_parser # type: ignore jc.lib.get_parser = lambda x: mock_parser # type: ignore
result = jc.lib.all_parser_info(show_hidden=True) result = jc.lib.all_parser_info(show_hidden=True)
# reset # reset
jc.lib.parsers = old_parsers jc.lib.parsers = old_parsers
jc.lib._get_parser = old_get_parser jc.lib.get_parser = old_get_parser
self.assertEqual(len(result), 1) self.assertEqual(len(result), 1)