1
0
mirror of https://github.com/kellyjonbrazil/jc.git synced 2026-04-03 17:44:07 +02:00

Compare commits

...

47 Commits

Author SHA1 Message Date
Kelly Brazil
2af61730f0 Merge pull request #68 from kellyjonbrazil/dev
Dev v1.11.5
2020-06-12 12:34:34 -07:00
Kelly Brazil
83f41b83dc version bump 2020-06-12 12:30:19 -07:00
Kelly Brazil
1fb84fce88 fix for no data 2020-06-12 12:25:07 -07:00
Kelly Brazil
a8837e1244 remove --upgrade from pip install 2020-06-12 07:57:40 -07:00
Kelly Brazil
04d2eec558 fix for no data 2020-06-11 17:59:06 -07:00
Kelly Brazil
1b57ec92f0 fix for no data 2020-06-11 17:52:03 -07:00
Kelly Brazil
4d88595404 enhance empty data check 2020-06-11 17:16:11 -07:00
Kelly Brazil
52b1272a3a enhance empty data check 2020-06-11 17:13:45 -07:00
Kelly Brazil
d2ccad6a83 fix for no data 2020-06-11 17:09:51 -07:00
Kelly Brazil
cad6dde4ac fix for no data 2020-06-10 17:54:06 -07:00
Kelly Brazil
06811c3539 add test for no data 2020-06-10 17:41:54 -07:00
Kelly Brazil
0cb23c2b21 add fix for no data 2020-06-10 17:40:18 -07:00
Kelly Brazil
ac4688dca2 add test for no data 2020-06-10 17:35:40 -07:00
Kelly Brazil
326c3b4670 add test for no data 2020-06-10 17:34:22 -07:00
Kelly Brazil
9b29d0c268 add test for no data 2020-06-10 17:32:39 -07:00
Kelly Brazil
e0013c3871 add test for no data 2020-06-10 17:31:14 -07:00
Kelly Brazil
a75744075b add no data test 2020-06-10 17:29:41 -07:00
Kelly Brazil
525aec1a02 fix for no data 2020-06-10 17:27:46 -07:00
Kelly Brazil
0bf9a7a072 add test for no data 2020-06-10 17:22:59 -07:00
Kelly Brazil
d8f2f4c95b fix for no data 2020-06-10 17:20:09 -07:00
Kelly Brazil
35d733b44f fix for no data 2020-06-10 17:10:53 -07:00
Kelly Brazil
9179b4175c add nodata tests 2020-06-10 16:40:11 -07:00
Kelly Brazil
bb07d78c78 add nodata fix 2020-06-10 16:39:49 -07:00
Kelly Brazil
07b179cd7f Merge pull request #67 from kellyjonbrazil/Dev
Dev v1.11.4
2020-06-10 06:07:42 -07:00
Kelly Brazil
054422d837 add test for empty directory 2020-06-10 06:04:50 -07:00
Kelly Brazil
3e052d1810 version bump 2020-06-10 05:53:20 -07:00
Kelly Brazil
c8e72805cf fix error on empty directory 2020-06-10 05:51:12 -07:00
Kelly Brazil
12a80e7db0 add fedora package info 2020-06-09 15:13:53 -07:00
Kelly Brazil
ee7ff9a09d Merge pull request #66 from kellyjonbrazil/dev
Dev v1.11.3
2020-06-09 11:22:39 -07:00
Kelly Brazil
f6478fb636 version bump 2020-06-09 11:18:47 -07:00
Kelly Brazil
811a0b0495 add info regarding the local parser plugin files 2020-06-08 10:54:42 -07:00
Kelly Brazil
aeb48edf72 use $LOCALAPPDATA variable for windows 2020-06-08 10:48:58 -07:00
Kelly Brazil
b1e94f0df7 heading formatting 2020-06-08 10:44:09 -07:00
Kelly Brazil
60050e3c0f fix linux/unix directory and add note about the XDG specification followed 2020-06-08 10:42:45 -07:00
Kelly Brazil
39ef09aa5b add local parser plugin feature 2020-06-07 13:30:22 -07:00
Kelly Brazil
8377d43116 formatting 2020-06-07 13:26:03 -07:00
Kelly Brazil
54e4c447ab clean up formatting 2020-06-07 12:52:16 -07:00
Kelly Brazil
937a9fa9cf vendorize appdirs module 2020-06-07 12:41:50 -07:00
Kelly Brazil
808ff6cf0e more acknowledgments updates 2020-06-07 12:29:10 -07:00
Kelly Brazil
7f5c649a95 update acknowledgments 2020-06-07 12:23:28 -07:00
Kelly Brazil
b72727dec9 update custom parsers info 2020-06-07 12:13:40 -07:00
Kelly Brazil
3fc88bfb33 Merge pull request #65 from duelafn/local-parsers
Load custom parsers from <user_data_dir>/jc/jcparsers
2020-06-07 12:04:44 -07:00
Dean Serenevy
9f2279d586 Load custom parsers from <user_data_dir>/jc/jcparsers 2020-06-06 14:42:27 -04:00
Kelly Brazil
346a14cb9b change osx_device to unix_device 2020-05-30 20:44:14 -07:00
Kelly Brazil
dac00d17ff add nixos test 2020-05-30 20:33:50 -07:00
Kelly Brazil
9ca7cd4060 update docs 2020-05-30 20:33:39 -07:00
Kelly Brazil
aa31628970 update docs 2020-05-30 20:33:00 -07:00
87 changed files with 1483 additions and 451 deletions

View File

@@ -70,21 +70,29 @@ Release notes can be found [here](https://blog.kellybrazil.com/category/jc-news/
For more information on the motivations for this project, please see my [blog post](https://blog.kellybrazil.com/2019/11/26/bringing-the-unix-philosophy-to-the-21st-century/).
## Installation
There are several ways to get `jc`. You can install via `pip`; other OS package repositories like `zypper`, `nix-env`, `brew`, or `portsnap`; via DEB/RPM packages; or by downloading the correct binary for your architecture and running it anywhere on your filesystem.
There are several ways to get `jc`. You can install via `pip`; other OS package repositories like `dnf`, `zypper`, `nix-env`, `brew`, or `portsnap`; via DEB/RPM packages; or by downloading the correct binary for your architecture and running it anywhere on your filesystem.
### Pip (macOS, linux, unix, Windows)
```
$ pip3 install --upgrade jc
$ pip3 install jc
```
### OS Package Repositories
#### Dnf (Fedora linux)
```
# dnf install jc
```
or
```
# dnf --enablerepo=updates-testing install jc
```
#### Zypper (openSUSE linux)
```
# zypper install jc
```
#### nix-env (NixOS linux)
#### Nix-env (NixOS linux)
```
$ nix-env -iA nixpkgs.jc
```
@@ -190,6 +198,19 @@ or
JC_COLORS=default,default,default,default
```
### Custom Parsers
Custom local parser plugins may be placed in a `jc/jcparsers` folder in your local **"App data directory"**:
- Linux/unix: `$HOME/.local/share/jc/jcparsers`
- macOS: `$HOME/Library/Application Support/jc/jcparsers`
- Windows: `$LOCALAPPDATA\jc\jc\jcparsers`
Local parser plugins are standard python module files. Use the [`jc/parsers/foo.py`](https://github.com/kellyjonbrazil/jc/blob/master/jc/parsers/foo.py) parser as a template and simply place a `.py` file in the `jcparsers` subfolder.
Local plugin filenames must be valid python module names, therefore must consist entirely of alphanumerics and start with a letter. Local plugins may override default plugins.
> Note: The application data directory follows the [XDG Base Directory Specification](https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html)
## Compatibility
Some parsers like `ls`, `ps`, `dig`, etc. will work on any platform. Other parsers that are platform-specific will generate a warning message if they are used on an unsupported platform. To see all parser information, including compatibility, run `jc -ap`.
@@ -212,12 +233,13 @@ Tested on:
Feel free to add/improve code or parsers! You can use the [`jc/parsers/foo.py`](https://github.com/kellyjonbrazil/jc/blob/master/jc/parsers/foo.py) parser as a template and submit your parser with a pull request.
## Acknowledgments
- CI automation and code optimizations from https://github.com/philippeitis
- `ifconfig-parser` module from https://github.com/KnightWhoSayNi/ifconfig-parser
- `xmltodict` module from https://github.com/martinblech/xmltodict by Martín Blech
- `ruamel.yaml` library from https://pypi.org/project/ruamel.yaml by Anthon van der Neut
- Parsing code from Conor Heine at https://gist.github.com/cahna/43a1a3ff4d075bcd71f9d7120037a501 adapted for some parsers
- Excellent constructive feedback from Ilya Sher (https://github.com/ilyash-b)
- Local parser plugin feature contributed by [Dean Serenevy](https://github.com/duelafn)
- CI automation and code optimizations by [philippeitis](https://github.com/philippeitis)
- [`ifconfig-parser`](https://github.com/KnightWhoSayNi/ifconfig-parser) module by KnightWhoSayNi
- [`xmltodict`](https://github.com/martinblech/xmltodict) module by Martín Blech
- [`ruamel.yaml`](https://pypi.org/project/ruamel.yaml) module by Anthon van der Neut
- Parsing [code](https://gist.github.com/cahna/43a1a3ff4d075bcd71f9d7120037a501) from Conor Heine adapted for some parsers
- Excellent constructive feedback from [Ilya Sher](https://github.com/ilyash-b)
## Examples
### airport -I
@@ -1699,7 +1721,11 @@ $ netstat -r | jc --netstat -p # or: jc -p netstat -r
"window": 0,
"irtt": 0,
"iface": "ens33",
"kind": "route"
"kind": "route",
"route_flags_pretty": [
"UP",
"GATEWAY"
]
},
{
"destination": "172.17.0.0",
@@ -1710,7 +1736,10 @@ $ netstat -r | jc --netstat -p # or: jc -p netstat -r
"window": 0,
"irtt": 0,
"iface": "docker0",
"kind": "route"
"kind": "route",
"route_flags_pretty": [
"UP"
]
},
{
"destination": "192.168.71.0",
@@ -1721,7 +1750,10 @@ $ netstat -r | jc --netstat -p # or: jc -p netstat -r
"window": 0,
"irtt": 0,
"iface": "ens33",
"kind": "route"
"kind": "route",
"route_flags_pretty": [
"UP"
]
}
]
@@ -1961,42 +1993,36 @@ $ route -ee | jc --route -p # or: jc -p route -ee
[
{
"destination": "default",
"gateway": "gateway",
"gateway": "_gateway",
"genmask": "0.0.0.0",
"flags": "UG",
"metric": 100,
"metric": 202,
"ref": 0,
"use": 0,
"iface": "ens33",
"mss": 0,
"window": 0,
"irtt": 0
},
{
"destination": "172.17.0.0",
"gateway": "0.0.0.0",
"genmask": "255.255.0.0",
"flags": "U",
"metric": 0,
"ref": 0,
"use": 0,
"iface": "docker",
"mss": 0,
"window": 0,
"irtt": 0
"irtt": 0,
"flags_pretty": [
"UP",
"GATEWAY"
]
},
{
"destination": "192.168.71.0",
"gateway": "0.0.0.0",
"genmask": "255.255.255.0",
"flags": "U",
"metric": 100,
"metric": 202,
"ref": 0,
"use": 0,
"iface": "ens33",
"mss": 0,
"window": 0,
"irtt": 0
"irtt": 0,
"flags_pretty": [
"UP"
]
}
]
```

View File

@@ -1,5 +1,36 @@
jc changelog
20200612 v1.11.5
- Update airport_s parser to fix error on parsing empty data
- Update arp parser to fix error on parsing empty data
- Update blkid parser to fix error on parsing empty data
- Update crontab parser to fix error on parsing empty data
- Update crontab_u parser to fix error on parsing empty data
- Update df parser to fix error on parsing empty data
- Update free parser to fix error on parsing empty data
- Update lsblk parser to fix error on parsing empty data
- Update lsmod parser to fix error on parsing empty data
- Update mount parser to fix error on parsing empty data
- Update netstat parser to fix error on parsing empty data
- Update ntpq parser to fix error on parsing empty data
- Update ps parser to fix error on parsing empty data
- Update route parser to fix error on parsing empty data
- Update systemctl parser to fix error on parsing empty data
- Update systemctl_lj parser to fix error on parsing empty data
- Update systemctl_ls parser to fix error on parsing empty data
- Update systemctl_luf parser to fix error on parsing empty data
- Update uptime parser to fix error on parsing empty data
- Update w parser to fix error on parsing empty data
- Update xml parser to fix error on parsing empty data
- Add tests to all parsers for no data condition
- Update ss parser to fix integer fields
20200610 v1.11.4
- Update ls parser to fix error on parsing an empty directory
20200609 v1.11.3
- Add local parser plugin feature (contributed by Dean Serenevy)
20200530 v1.11.2
- Update netstat parser to add freebsd support
- Update netstat parser to add route_flags_pretty field
@@ -12,7 +43,7 @@ jc changelog
- Update w parser to strip whitespace from what field
- Update last parser to fix FreeBSD issues
- Update stat parser to change osx_flags field name to unix_flags
- Update stat parser to add osx_device field for freebsd and osx
- Update stat parser to add unix_device field for freebsd and osx
- Fix freebsd compatibility message for df, fstab, mount, ntpq, stat, and uname parsers
- Fix compatibility message for platforms that include the version number at the end (e.g. freebsd12)

View File

@@ -177,7 +177,11 @@ Examples:
"window": 0,
"irtt": 0,
"iface": "ens33",
"kind": "route"
"kind": "route",
"route_flags_pretty": [
"UP",
"GATEWAY"
]
},
{
"destination": "172.17.0.0",
@@ -188,7 +192,10 @@ Examples:
"window": 0,
"irtt": 0,
"iface": "docker0",
"kind": "route"
"kind": "route",
"route_flags_pretty": [
"UP"
]
},
{
"destination": "192.168.71.0",
@@ -199,7 +206,10 @@ Examples:
"window": 0,
"irtt": 0,
"iface": "ens33",
"kind": "route"
"kind": "route",
"route_flags_pretty": [
"UP"
]
}
]

View File

@@ -15,53 +15,48 @@ Examples:
[
{
"destination": "default",
"gateway": "gateway",
"gateway": "_gateway",
"genmask": "0.0.0.0",
"flags": "UG",
"metric": 100,
"metric": 202,
"ref": 0,
"use": 0,
"iface": "ens33",
"mss": 0,
"window": 0,
"irtt": 0
},
{
"destination": "172.17.0.0",
"gateway": "0.0.0.0",
"genmask": "255.255.0.0",
"flags": "U",
"metric": 0,
"ref": 0,
"use": 0,
"iface": "docker",
"mss": 0,
"window": 0,
"irtt": 0
"irtt": 0,
"flags_pretty": [
"UP",
"GATEWAY"
]
},
{
"destination": "192.168.71.0",
"gateway": "0.0.0.0",
"genmask": "255.255.255.0",
"flags": "U",
"metric": 100,
"metric": 202,
"ref": 0,
"use": 0,
"iface": "ens33",
"mss": 0,
"window": 0,
"irtt": 0
"irtt": 0,
"flags_pretty": [
"UP"
]
}
]
$ route -ee | jc --route -p -r
[
{
"destination": "default",
"gateway": "gateway",
"gateway": "_gateway",
"genmask": "0.0.0.0",
"flags": "UG",
"metric": "100",
"metric": "202",
"ref": "0",
"use": "0",
"iface": "ens33",
@@ -69,25 +64,12 @@ Examples:
"window": "0",
"irtt": "0"
},
{
"destination": "172.17.0.0",
"gateway": "0.0.0.0",
"genmask": "255.255.0.0",
"flags": "U",
"metric": "0",
"ref": "0",
"use": "0",
"iface": "docker",
"mss": "0",
"window": "0",
"irtt": "0"
},
{
"destination": "192.168.71.0",
"gateway": "0.0.0.0",
"genmask": "255.255.255.0",
"flags": "U",
"metric": "100",
"metric": "202",
"ref": "0",
"use": "0",
"iface": "ens33",
@@ -97,6 +79,7 @@ Examples:
}
]
## info
```python
info(self, /, *args, **kwargs)

View File

@@ -142,10 +142,10 @@ Returns:
"modify_time": string, # - = null
"change_time": string, # - = null
"birth_time": string, # - = null
"osx_device": integer,
"unix_device": integer,
"rdev": integer,
"block_size": integer,
"osx_flags": string
"unix_flags": string
}
]

611
jc/appdirs.py Normal file
View File

@@ -0,0 +1,611 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2005-2010 ActiveState Software Inc.
# Copyright (c) 2013 Eddy Petrișor
'''
# This is the MIT license
Copyright (c) 2010 ActiveState Software Inc.
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:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
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.
'''
"""Utilities for determining application-specific dirs.
See <https://github.com/ActiveState/appdirs> for details and usage.
"""
# Dev Notes:
# - MSDN on where to store app data files:
# http://support.microsoft.com/default.aspx?scid=kb;en-us;310294#XSLTH3194121123120121120120
# - Mac OS X: http://developer.apple.com/documentation/MacOSX/Conceptual/BPFileSystem/index.html
# - XDG spec for Un*x: https://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html
__version__ = "1.4.4"
__version_info__ = tuple(int(segment) for segment in __version__.split("."))
import sys
import os
PY3 = sys.version_info[0] == 3
if PY3:
unicode = str
if sys.platform.startswith('java'):
import platform
os_name = platform.java_ver()[3][0]
if os_name.startswith('Windows'): # "Windows XP", "Windows 7", etc.
system = 'win32'
elif os_name.startswith('Mac'): # "Mac OS X", etc.
system = 'darwin'
else: # "Linux", "SunOS", "FreeBSD", etc.
# Setting this to "linux2" is not ideal, but only Windows or Mac
# are actually checked for and the rest of the module expects
# *sys.platform* style strings.
system = 'linux2'
else:
system = sys.platform
def user_data_dir(appname=None, appauthor=None, version=None, roaming=False):
r"""Return full path to the user-specific data dir for this application.
"appname" is the name of application.
If None, just the system directory is returned.
"appauthor" (only used on Windows) is the name of the
appauthor or distributing body for this application. Typically
it is the owning company name. This falls back to appname. You may
pass False to disable it.
"version" is an optional version path element to append to the
path. You might want to use this if you want multiple versions
of your app to be able to run independently. If used, this
would typically be "<major>.<minor>".
Only applied when appname is present.
"roaming" (boolean, default False) can be set True to use the Windows
roaming appdata directory. That means that for users on a Windows
network setup for roaming profiles, this user data will be
sync'd on login. See
<http://technet.microsoft.com/en-us/library/cc766489(WS.10).aspx>
for a discussion of issues.
Typical user data directories are:
Mac OS X: ~/Library/Application Support/<AppName>
Unix: ~/.local/share/<AppName> # or in $XDG_DATA_HOME, if defined
Win XP (not roaming): C:\Documents and Settings\<username>\Application Data\<AppAuthor>\<AppName>
Win XP (roaming): C:\Documents and Settings\<username>\Local Settings\Application Data\<AppAuthor>\<AppName>
Win 7 (not roaming): C:\Users\<username>\AppData\Local\<AppAuthor>\<AppName>
Win 7 (roaming): C:\Users\<username>\AppData\Roaming\<AppAuthor>\<AppName>
For Unix, we follow the XDG spec and support $XDG_DATA_HOME.
That means, by default "~/.local/share/<AppName>".
"""
if system == "win32":
if appauthor is None:
appauthor = appname
const = roaming and "CSIDL_APPDATA" or "CSIDL_LOCAL_APPDATA"
path = os.path.normpath(_get_win_folder(const))
if appname:
if appauthor is not False:
path = os.path.join(path, appauthor, appname)
else:
path = os.path.join(path, appname)
elif system == 'darwin':
path = os.path.expanduser('~/Library/Application Support/')
if appname:
path = os.path.join(path, appname)
else:
path = os.getenv('XDG_DATA_HOME', os.path.expanduser("~/.local/share"))
if appname:
path = os.path.join(path, appname)
if appname and version:
path = os.path.join(path, version)
return path
def site_data_dir(appname=None, appauthor=None, version=None, multipath=False):
r"""Return full path to the user-shared data dir for this application.
"appname" is the name of application.
If None, just the system directory is returned.
"appauthor" (only used on Windows) is the name of the
appauthor or distributing body for this application. Typically
it is the owning company name. This falls back to appname. You may
pass False to disable it.
"version" is an optional version path element to append to the
path. You might want to use this if you want multiple versions
of your app to be able to run independently. If used, this
would typically be "<major>.<minor>".
Only applied when appname is present.
"multipath" is an optional parameter only applicable to *nix
which indicates that the entire list of data dirs should be
returned. By default, the first item from XDG_DATA_DIRS is
returned, or '/usr/local/share/<AppName>',
if XDG_DATA_DIRS is not set
Typical site data directories are:
Mac OS X: /Library/Application Support/<AppName>
Unix: /usr/local/share/<AppName> or /usr/share/<AppName>
Win XP: C:\Documents and Settings\All Users\Application Data\<AppAuthor>\<AppName>
Vista: (Fail! "C:\ProgramData" is a hidden *system* directory on Vista.)
Win 7: C:\ProgramData\<AppAuthor>\<AppName> # Hidden, but writeable on Win 7.
For Unix, this is using the $XDG_DATA_DIRS[0] default.
WARNING: Do not use this on Windows. See the Vista-Fail note above for why.
"""
if system == "win32":
if appauthor is None:
appauthor = appname
path = os.path.normpath(_get_win_folder("CSIDL_COMMON_APPDATA"))
if appname:
if appauthor is not False:
path = os.path.join(path, appauthor, appname)
else:
path = os.path.join(path, appname)
elif system == 'darwin':
path = os.path.expanduser('/Library/Application Support')
if appname:
path = os.path.join(path, appname)
else:
# XDG default for $XDG_DATA_DIRS
# only first, if multipath is False
path = os.getenv('XDG_DATA_DIRS',
os.pathsep.join(['/usr/local/share', '/usr/share']))
pathlist = [os.path.expanduser(x.rstrip(os.sep)) for x in path.split(os.pathsep)]
if appname:
if version:
appname = os.path.join(appname, version)
pathlist = [os.sep.join([x, appname]) for x in pathlist]
if multipath:
path = os.pathsep.join(pathlist)
else:
path = pathlist[0]
return path
if appname and version:
path = os.path.join(path, version)
return path
def user_config_dir(appname=None, appauthor=None, version=None, roaming=False):
r"""Return full path to the user-specific config dir for this application.
"appname" is the name of application.
If None, just the system directory is returned.
"appauthor" (only used on Windows) is the name of the
appauthor or distributing body for this application. Typically
it is the owning company name. This falls back to appname. You may
pass False to disable it.
"version" is an optional version path element to append to the
path. You might want to use this if you want multiple versions
of your app to be able to run independently. If used, this
would typically be "<major>.<minor>".
Only applied when appname is present.
"roaming" (boolean, default False) can be set True to use the Windows
roaming appdata directory. That means that for users on a Windows
network setup for roaming profiles, this user data will be
sync'd on login. See
<http://technet.microsoft.com/en-us/library/cc766489(WS.10).aspx>
for a discussion of issues.
Typical user config directories are:
Mac OS X: ~/Library/Preferences/<AppName>
Unix: ~/.config/<AppName> # or in $XDG_CONFIG_HOME, if defined
Win *: same as user_data_dir
For Unix, we follow the XDG spec and support $XDG_CONFIG_HOME.
That means, by default "~/.config/<AppName>".
"""
if system == "win32":
path = user_data_dir(appname, appauthor, None, roaming)
elif system == 'darwin':
path = os.path.expanduser('~/Library/Preferences/')
if appname:
path = os.path.join(path, appname)
else:
path = os.getenv('XDG_CONFIG_HOME', os.path.expanduser("~/.config"))
if appname:
path = os.path.join(path, appname)
if appname and version:
path = os.path.join(path, version)
return path
def site_config_dir(appname=None, appauthor=None, version=None, multipath=False):
r"""Return full path to the user-shared data dir for this application.
"appname" is the name of application.
If None, just the system directory is returned.
"appauthor" (only used on Windows) is the name of the
appauthor or distributing body for this application. Typically
it is the owning company name. This falls back to appname. You may
pass False to disable it.
"version" is an optional version path element to append to the
path. You might want to use this if you want multiple versions
of your app to be able to run independently. If used, this
would typically be "<major>.<minor>".
Only applied when appname is present.
"multipath" is an optional parameter only applicable to *nix
which indicates that the entire list of config dirs should be
returned. By default, the first item from XDG_CONFIG_DIRS is
returned, or '/etc/xdg/<AppName>', if XDG_CONFIG_DIRS is not set
Typical site config directories are:
Mac OS X: same as site_data_dir
Unix: /etc/xdg/<AppName> or $XDG_CONFIG_DIRS[i]/<AppName> for each value in
$XDG_CONFIG_DIRS
Win *: same as site_data_dir
Vista: (Fail! "C:\ProgramData" is a hidden *system* directory on Vista.)
For Unix, this is using the $XDG_CONFIG_DIRS[0] default, if multipath=False
WARNING: Do not use this on Windows. See the Vista-Fail note above for why.
"""
if system == 'win32':
path = site_data_dir(appname, appauthor)
if appname and version:
path = os.path.join(path, version)
elif system == 'darwin':
path = os.path.expanduser('/Library/Preferences')
if appname:
path = os.path.join(path, appname)
else:
# XDG default for $XDG_CONFIG_DIRS
# only first, if multipath is False
path = os.getenv('XDG_CONFIG_DIRS', '/etc/xdg')
pathlist = [os.path.expanduser(x.rstrip(os.sep)) for x in path.split(os.pathsep)]
if appname:
if version:
appname = os.path.join(appname, version)
pathlist = [os.sep.join([x, appname]) for x in pathlist]
if multipath:
path = os.pathsep.join(pathlist)
else:
path = pathlist[0]
return path
def user_cache_dir(appname=None, appauthor=None, version=None, opinion=True):
r"""Return full path to the user-specific cache dir for this application.
"appname" is the name of application.
If None, just the system directory is returned.
"appauthor" (only used on Windows) is the name of the
appauthor or distributing body for this application. Typically
it is the owning company name. This falls back to appname. You may
pass False to disable it.
"version" is an optional version path element to append to the
path. You might want to use this if you want multiple versions
of your app to be able to run independently. If used, this
would typically be "<major>.<minor>".
Only applied when appname is present.
"opinion" (boolean) can be False to disable the appending of
"Cache" to the base app data dir for Windows. See
discussion below.
Typical user cache directories are:
Mac OS X: ~/Library/Caches/<AppName>
Unix: ~/.cache/<AppName> (XDG default)
Win XP: C:\Documents and Settings\<username>\Local Settings\Application Data\<AppAuthor>\<AppName>\Cache
Vista: C:\Users\<username>\AppData\Local\<AppAuthor>\<AppName>\Cache
On Windows the only suggestion in the MSDN docs is that local settings go in
the `CSIDL_LOCAL_APPDATA` directory. This is identical to the non-roaming
app data dir (the default returned by `user_data_dir` above). Apps typically
put cache data somewhere *under* the given dir here. Some examples:
...\Mozilla\Firefox\Profiles\<ProfileName>\Cache
...\Acme\SuperApp\Cache\1.0
OPINION: This function appends "Cache" to the `CSIDL_LOCAL_APPDATA` value.
This can be disabled with the `opinion=False` option.
"""
if system == "win32":
if appauthor is None:
appauthor = appname
path = os.path.normpath(_get_win_folder("CSIDL_LOCAL_APPDATA"))
if appname:
if appauthor is not False:
path = os.path.join(path, appauthor, appname)
else:
path = os.path.join(path, appname)
if opinion:
path = os.path.join(path, "Cache")
elif system == 'darwin':
path = os.path.expanduser('~/Library/Caches')
if appname:
path = os.path.join(path, appname)
else:
path = os.getenv('XDG_CACHE_HOME', os.path.expanduser('~/.cache'))
if appname:
path = os.path.join(path, appname)
if appname and version:
path = os.path.join(path, version)
return path
def user_state_dir(appname=None, appauthor=None, version=None, roaming=False):
r"""Return full path to the user-specific state dir for this application.
"appname" is the name of application.
If None, just the system directory is returned.
"appauthor" (only used on Windows) is the name of the
appauthor or distributing body for this application. Typically
it is the owning company name. This falls back to appname. You may
pass False to disable it.
"version" is an optional version path element to append to the
path. You might want to use this if you want multiple versions
of your app to be able to run independently. If used, this
would typically be "<major>.<minor>".
Only applied when appname is present.
"roaming" (boolean, default False) can be set True to use the Windows
roaming appdata directory. That means that for users on a Windows
network setup for roaming profiles, this user data will be
sync'd on login. See
<http://technet.microsoft.com/en-us/library/cc766489(WS.10).aspx>
for a discussion of issues.
Typical user state directories are:
Mac OS X: same as user_data_dir
Unix: ~/.local/state/<AppName> # or in $XDG_STATE_HOME, if defined
Win *: same as user_data_dir
For Unix, we follow this Debian proposal <https://wiki.debian.org/XDGBaseDirectorySpecification#state>
to extend the XDG spec and support $XDG_STATE_HOME.
That means, by default "~/.local/state/<AppName>".
"""
if system in ["win32", "darwin"]:
path = user_data_dir(appname, appauthor, None, roaming)
else:
path = os.getenv('XDG_STATE_HOME', os.path.expanduser("~/.local/state"))
if appname:
path = os.path.join(path, appname)
if appname and version:
path = os.path.join(path, version)
return path
def user_log_dir(appname=None, appauthor=None, version=None, opinion=True):
r"""Return full path to the user-specific log dir for this application.
"appname" is the name of application.
If None, just the system directory is returned.
"appauthor" (only used on Windows) is the name of the
appauthor or distributing body for this application. Typically
it is the owning company name. This falls back to appname. You may
pass False to disable it.
"version" is an optional version path element to append to the
path. You might want to use this if you want multiple versions
of your app to be able to run independently. If used, this
would typically be "<major>.<minor>".
Only applied when appname is present.
"opinion" (boolean) can be False to disable the appending of
"Logs" to the base app data dir for Windows, and "log" to the
base cache dir for Unix. See discussion below.
Typical user log directories are:
Mac OS X: ~/Library/Logs/<AppName>
Unix: ~/.cache/<AppName>/log # or under $XDG_CACHE_HOME if defined
Win XP: C:\Documents and Settings\<username>\Local Settings\Application Data\<AppAuthor>\<AppName>\Logs
Vista: C:\Users\<username>\AppData\Local\<AppAuthor>\<AppName>\Logs
On Windows the only suggestion in the MSDN docs is that local settings
go in the `CSIDL_LOCAL_APPDATA` directory. (Note: I'm interested in
examples of what some windows apps use for a logs dir.)
OPINION: This function appends "Logs" to the `CSIDL_LOCAL_APPDATA`
value for Windows and appends "log" to the user cache dir for Unix.
This can be disabled with the `opinion=False` option.
"""
if system == "darwin":
path = os.path.join(
os.path.expanduser('~/Library/Logs'),
appname)
elif system == "win32":
path = user_data_dir(appname, appauthor, version)
version = False
if opinion:
path = os.path.join(path, "Logs")
else:
path = user_cache_dir(appname, appauthor, version)
version = False
if opinion:
path = os.path.join(path, "log")
if appname and version:
path = os.path.join(path, version)
return path
class AppDirs(object):
"""Convenience wrapper for getting application dirs."""
def __init__(self, appname=None, appauthor=None, version=None,
roaming=False, multipath=False):
self.appname = appname
self.appauthor = appauthor
self.version = version
self.roaming = roaming
self.multipath = multipath
@property
def user_data_dir(self):
return user_data_dir(self.appname, self.appauthor,
version=self.version, roaming=self.roaming)
@property
def site_data_dir(self):
return site_data_dir(self.appname, self.appauthor,
version=self.version, multipath=self.multipath)
@property
def user_config_dir(self):
return user_config_dir(self.appname, self.appauthor,
version=self.version, roaming=self.roaming)
@property
def site_config_dir(self):
return site_config_dir(self.appname, self.appauthor,
version=self.version, multipath=self.multipath)
@property
def user_cache_dir(self):
return user_cache_dir(self.appname, self.appauthor,
version=self.version)
@property
def user_state_dir(self):
return user_state_dir(self.appname, self.appauthor,
version=self.version)
@property
def user_log_dir(self):
return user_log_dir(self.appname, self.appauthor,
version=self.version)
#---- internal support stuff
def _get_win_folder_from_registry(csidl_name):
"""This is a fallback technique at best. I'm not sure if using the
registry for this guarantees us the correct answer for all CSIDL_*
names.
"""
if PY3:
import winreg as _winreg
else:
import _winreg
shell_folder_name = {
"CSIDL_APPDATA": "AppData",
"CSIDL_COMMON_APPDATA": "Common AppData",
"CSIDL_LOCAL_APPDATA": "Local AppData",
}[csidl_name]
key = _winreg.OpenKey(
_winreg.HKEY_CURRENT_USER,
r"Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders"
)
dir, type = _winreg.QueryValueEx(key, shell_folder_name)
return dir
def _get_win_folder_with_ctypes(csidl_name):
import ctypes
csidl_const = {
"CSIDL_APPDATA": 26,
"CSIDL_COMMON_APPDATA": 35,
"CSIDL_LOCAL_APPDATA": 28,
}[csidl_name]
buf = ctypes.create_unicode_buffer(1024)
ctypes.windll.shell32.SHGetFolderPathW(None, csidl_const, None, 0, buf)
# Downgrade to short path name if have highbit chars. See
# <http://bugs.activestate.com/show_bug.cgi?id=85099>.
has_high_char = False
for c in buf:
if ord(c) > 255:
has_high_char = True
break
if has_high_char:
buf2 = ctypes.create_unicode_buffer(1024)
if ctypes.windll.kernel32.GetShortPathNameW(buf.value, buf2, 1024):
buf = buf2
return buf.value
def _get_win_folder_with_jna(csidl_name):
import array
from com.sun import jna
from com.sun.jna.platform import win32
buf_size = win32.WinDef.MAX_PATH * 2
buf = array.zeros('c', buf_size)
shell = win32.Shell32.INSTANCE
shell.SHGetFolderPath(None, getattr(win32.ShlObj, csidl_name), None, win32.ShlObj.SHGFP_TYPE_CURRENT, buf)
dir = jna.Native.toString(buf.tostring()).rstrip("\0")
# Downgrade to short path name if have highbit chars. See
# <http://bugs.activestate.com/show_bug.cgi?id=85099>.
has_high_char = False
for c in dir:
if ord(c) > 255:
has_high_char = True
break
if has_high_char:
buf = array.zeros('c', buf_size)
kernel = win32.Kernel32.INSTANCE
if kernel.GetShortPathName(dir, buf, buf_size):
dir = jna.Native.toString(buf.tostring()).rstrip("\0")
return dir
if system == "win32":
try:
from ctypes import windll
except ImportError:
try:
import com.sun.jna
except ImportError:
_get_win_folder = _get_win_folder_from_registry
else:
_get_win_folder = _get_win_folder_with_jna
else:
_get_win_folder = _get_win_folder_with_ctypes
#---- self test code
if __name__ == "__main__":
appname = "MyApp"
appauthor = "MyCompany"
props = ("user_data_dir",
"user_config_dir",
"user_cache_dir",
"user_state_dir",
"user_log_dir",
"site_data_dir",
"site_config_dir")
print("-- app dirs %s --" % __version__)
print("-- app dirs (with optional 'version')")
dirs = AppDirs(appname, appauthor, version="1.0")
for prop in props:
print("%s: %s" % (prop, getattr(dirs, prop)))
print("\n-- app dirs (without optional 'version')")
dirs = AppDirs(appname, appauthor)
for prop in props:
print("%s: %s" % (prop, getattr(dirs, prop)))
print("\n-- app dirs (without optional 'appauthor')")
dirs = AppDirs(appname)
for prop in props:
print("%s: %s" % (prop, getattr(dirs, prop)))
print("\n-- app dirs (with disabled 'appauthor')")
dirs = AppDirs(appname, appauthor=False)
for prop in props:
print("%s: %s" % (prop, getattr(dirs, prop)))

View File

@@ -1,8 +1,11 @@
"""jc - JSON CLI output utility
JC cli module
"""
import sys
import os
import os.path
import re
import shlex
import importlib
import textwrap
@@ -14,10 +17,11 @@ from pygments.token import (Name, Number, String, Keyword)
from pygments.lexers import JsonLexer
from pygments.formatters import Terminal256Formatter
import jc.utils
import jc.appdirs as appdirs
class info():
version = '1.11.2'
version = '1.11.5'
description = 'jc cli output JSON conversion tool'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
@@ -79,6 +83,20 @@ parsers = [
'yaml'
]
# List of custom or override parsers.
# Allow any <user_data_dir>/jc/jcparsers/*.py
local_parsers = []
data_dir = appdirs.user_data_dir("jc", "jc")
local_parsers_dir = os.path.join(data_dir, "jcparsers")
if os.path.isdir(local_parsers_dir):
sys.path.append(data_dir)
for name in os.listdir(local_parsers_dir):
if re.match(r'\w+\.py', name) and os.path.isfile(os.path.join(local_parsers_dir, name)):
plugin_name = name[0:-3]
local_parsers.append(plugin_name)
if plugin_name not in parsers:
parsers.append(plugin_name)
def set_env_colors():
"""
@@ -122,10 +140,10 @@ def set_env_colors():
# Try the color set in the JC_COLORS env variable first. If it is set to default, then fall back to default colors
return {
Name.Tag: f'bold ansi{color_list[0]}' if not color_list[0] == 'default' else f'bold ansiblue', # key names
Keyword: f'ansi{color_list[1]}' if not color_list[1] == 'default' else f'ansibrightblack', # true, false, null
Number: f'ansi{color_list[2]}' if not color_list[2] == 'default' else f'ansimagenta', # numbers
String: f'ansi{color_list[3]}' if not color_list[3] == 'default' else f'ansigreen' # strings
Name.Tag: f'bold ansi{color_list[0]}' if not color_list[0] == 'default' else 'bold ansiblue', # key names
Keyword: f'ansi{color_list[1]}' if not color_list[1] == 'default' else 'ansibrightblack', # true, false, null
Number: f'ansi{color_list[2]}' if not color_list[2] == 'default' else 'ansimagenta', # numbers
String: f'ansi{color_list[3]}' if not color_list[3] == 'default' else 'ansigreen' # strings
}
@@ -159,8 +177,9 @@ def parser_mod_shortname(parser):
def parser_module(parser):
"""import the module just in time and return the module object"""
importlib.import_module('jc.parsers.' + parser_mod_shortname(parser))
return getattr(jc.parsers, parser_mod_shortname(parser))
shortname = parser_mod_shortname(parser)
path = ('jcparsers.' if shortname in local_parsers else 'jc.parsers.')
return importlib.import_module(path + shortname)
def parsers_text(indent=0, pad=0):
@@ -245,7 +264,7 @@ def helptext(message):
def json_out(data, pretty=False, mono=False, piped_out=False):
# set colors
# set colors
class JcStyle(Style):
styles = set_env_colors()

View File

@@ -88,7 +88,7 @@ import jc.parsers.universal
class info():
version = '1.0'
version = '1.1'
description = 'airport -s command parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
@@ -170,15 +170,17 @@ def parse(data, raw=False, quiet=False):
if not quiet:
jc.utils.compatibility(__name__, info.compatible)
cleandata = data.splitlines()
raw_output = []
cleandata = list(filter(None, data.splitlines()))
# fix headers
cleandata[0] = cleandata[0].lower()
cleandata[0] = cleandata[0].replace('-', '_')
cleandata[0] = cleandata[0].replace('security (auth/unicast/group)', 'security')
if cleandata:
# fix headers
cleandata[0] = cleandata[0].lower()
cleandata[0] = cleandata[0].replace('-', '_')
cleandata[0] = cleandata[0].replace('security (auth/unicast/group)', 'security')
# parse the data
raw_output = jc.parsers.universal.sparse_table_parse(cleandata)
# parse the data
raw_output = jc.parsers.universal.sparse_table_parse(cleandata)
if raw:
return raw_output

View File

@@ -99,7 +99,7 @@ import jc.parsers.universal
class info():
version = '1.4'
version = '1.5'
description = 'arp command parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
@@ -171,69 +171,65 @@ def parse(data, raw=False, quiet=False):
if not quiet:
jc.utils.compatibility(__name__, info.compatible)
cleandata = data.splitlines()
raw_output = []
cleandata = list(filter(None, data.splitlines()))
# remove final Entries row if -v was used
if cleandata[-1].startswith('Entries:'):
cleandata.pop(-1)
if cleandata:
# detect if freebsd/osx style was used
if cleandata[0][-1] == ']':
raw_output = []
for line in cleandata:
splitline = line.split()
output_line = {
'name': splitline[0],
'address': splitline[1].lstrip('(').rstrip(')'),
'hwtype': splitline[-1].lstrip('[').rstrip(']'),
'hwaddress': splitline[3],
'iface': splitline[5]
}
# remove final Entries row if -v was used
if cleandata[-1].startswith('Entries:'):
cleandata.pop(-1)
if 'permanent' in splitline:
output_line['permanent'] = True
# detect if freebsd/osx style was used
if cleandata[0][-1] == ']':
for line in cleandata:
splitline = line.split()
output_line = {
'name': splitline[0],
'address': splitline[1].lstrip('(').rstrip(')'),
'hwtype': splitline[-1].lstrip('[').rstrip(']'),
'hwaddress': splitline[3],
'iface': splitline[5]
}
if 'permanent' in splitline:
output_line['permanent'] = True
else:
output_line['permanent'] = False
if 'expires' in splitline:
output_line['expires'] = splitline[-3]
raw_output.append(output_line)
if raw:
return raw_output
else:
output_line['permanent'] = False
return process(raw_output)
if 'expires' in splitline:
output_line['expires'] = splitline[-3]
# detect if linux style was used
elif cleandata[0].startswith('Address'):
raw_output.append(output_line)
# fix header row to change Flags Mask to flags_mask
cleandata[0] = cleandata[0].replace('Flags Mask', 'flags_mask')
cleandata[0] = cleandata[0].lower()
if raw:
return raw_output
raw_output = jc.parsers.universal.simple_table_parse(cleandata)
# otherwise, try bsd style
else:
return process(raw_output)
for line in cleandata:
line = line.split()
output_line = {
'name': line[0],
'address': line[1].lstrip('(').rstrip(')'),
'hwtype': line[4].lstrip('[').rstrip(']'),
'hwaddress': line[3],
'iface': line[6],
}
raw_output.append(output_line)
# detect if linux style was used
elif cleandata[0].startswith('Address'):
# fix header row to change Flags Mask to flags_mask
cleandata[0] = cleandata[0].replace('Flags Mask', 'flags_mask')
cleandata[0] = cleandata[0].lower()
raw_output = jc.parsers.universal.simple_table_parse(cleandata)
if raw:
return raw_output
else:
return process(raw_output)
# otherwise, try bsd style
if raw:
return raw_output
else:
raw_output = []
for line in cleandata:
line = line.split()
output_line = {
'name': line[0],
'address': line[1].lstrip('(').rstrip(')'),
'hwtype': line[4].lstrip('[').rstrip(']'),
'hwaddress': line[3],
'iface': line[6],
}
raw_output.append(output_line)
if raw:
return raw_output
else:
return process(raw_output)
return process(raw_output)

View File

@@ -79,7 +79,7 @@ import jc.utils
class info():
version = '1.0'
version = '1.1'
description = 'blkid command parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
@@ -176,7 +176,7 @@ def parse(data, raw=False, quiet=False):
raw_output = []
if data:
if list(filter(None, data.splitlines())):
# if the first field is a device, use normal parsing:
if data.split(maxsplit=1)[0][-1] == ':':
linedata = data.splitlines()

View File

@@ -132,7 +132,7 @@ import jc.parsers.universal
class info():
version = '1.2'
version = '1.3'
description = 'crontab command and file parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
@@ -225,44 +225,45 @@ def parse(data, raw=False, quiet=False):
# Clear any blank lines
cleandata = list(filter(None, cleandata))
# Clear any commented lines
for i, line in reversed(list(enumerate(cleandata))):
if line.strip().startswith('#'):
cleandata.pop(i)
if cleandata:
# Clear any commented lines
for i, line in reversed(list(enumerate(cleandata))):
if line.strip().startswith('#'):
cleandata.pop(i)
# Pop any variable assignment lines
cron_var = []
for i, line in reversed(list(enumerate(cleandata))):
if '=' in line:
var_line = cleandata.pop(i)
var_name = var_line.split('=', maxsplit=1)[0].strip()
var_value = var_line.split('=', maxsplit=1)[1].strip()
cron_var.append({'name': var_name,
'value': var_value})
# Pop any variable assignment lines
cron_var = []
for i, line in reversed(list(enumerate(cleandata))):
if '=' in line:
var_line = cleandata.pop(i)
var_name = var_line.split('=', maxsplit=1)[0].strip()
var_value = var_line.split('=', maxsplit=1)[1].strip()
cron_var.append({'name': var_name,
'value': var_value})
raw_output['variables'] = cron_var
raw_output['variables'] = cron_var
# Pop any shortcut lines
shortcut_list = []
for i, line in reversed(list(enumerate(cleandata))):
if line.strip().startswith('@'):
shortcut_line = cleandata.pop(i)
occurrence = shortcut_line.split(maxsplit=1)[0].strip().lstrip('@')
cmd = shortcut_line.split(maxsplit=1)[1].strip()
shortcut_list.append({'occurrence': occurrence,
'command': cmd})
# Pop any shortcut lines
shortcut_list = []
for i, line in reversed(list(enumerate(cleandata))):
if line.strip().startswith('@'):
shortcut_line = cleandata.pop(i)
occurrence = shortcut_line.split(maxsplit=1)[0].strip().lstrip('@')
cmd = shortcut_line.split(maxsplit=1)[1].strip()
shortcut_list.append({'occurrence': occurrence,
'command': cmd})
# Add header row for parsing
cleandata[:0] = ['minute hour day_of_month month day_of_week command']
# Add header row for parsing
cleandata[:0] = ['minute hour day_of_month month day_of_week command']
if len(cleandata) > 1:
cron_list = jc.parsers.universal.simple_table_parse(cleandata)
if len(cleandata) > 1:
cron_list = jc.parsers.universal.simple_table_parse(cleandata)
raw_output['schedule'] = cron_list
raw_output['schedule'] = cron_list
# Add shortcut entries back in
for item in shortcut_list:
raw_output['schedule'].append(item)
# Add shortcut entries back in
for item in shortcut_list:
raw_output['schedule'].append(item)
if raw:
return raw_output

View File

@@ -133,7 +133,7 @@ import jc.parsers.universal
class info():
version = '1.1'
version = '1.2'
description = 'crontab file parser with user support'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
@@ -226,46 +226,47 @@ def parse(data, raw=False, quiet=False):
# Clear any blank lines
cleandata = list(filter(None, cleandata))
# Clear any commented lines
for i, line in reversed(list(enumerate(cleandata))):
if line.strip().startswith('#'):
cleandata.pop(i)
if cleandata:
# Clear any commented lines
for i, line in reversed(list(enumerate(cleandata))):
if line.strip().startswith('#'):
cleandata.pop(i)
# Pop any variable assignment lines
cron_var = []
for i, line in reversed(list(enumerate(cleandata))):
if '=' in line:
var_line = cleandata.pop(i)
var_name = var_line.split('=', maxsplit=1)[0].strip()
var_value = var_line.split('=', maxsplit=1)[1].strip()
cron_var.append({'name': var_name,
'value': var_value})
# Pop any variable assignment lines
cron_var = []
for i, line in reversed(list(enumerate(cleandata))):
if '=' in line:
var_line = cleandata.pop(i)
var_name = var_line.split('=', maxsplit=1)[0].strip()
var_value = var_line.split('=', maxsplit=1)[1].strip()
cron_var.append({'name': var_name,
'value': var_value})
raw_output['variables'] = cron_var
raw_output['variables'] = cron_var
# Pop any shortcut lines
shortcut_list = []
for i, line in reversed(list(enumerate(cleandata))):
if line.strip().startswith('@'):
shortcut_line = cleandata.pop(i)
occurrence = shortcut_line.split(maxsplit=1)[0].strip().lstrip('@')
usr = shortcut_line.split(maxsplit=2)[1].strip()
cmd = shortcut_line.split(maxsplit=2)[2].strip()
shortcut_list.append({'occurrence': occurrence,
'user': usr,
'command': cmd})
# Pop any shortcut lines
shortcut_list = []
for i, line in reversed(list(enumerate(cleandata))):
if line.strip().startswith('@'):
shortcut_line = cleandata.pop(i)
occurrence = shortcut_line.split(maxsplit=1)[0].strip().lstrip('@')
usr = shortcut_line.split(maxsplit=2)[1].strip()
cmd = shortcut_line.split(maxsplit=2)[2].strip()
shortcut_list.append({'occurrence': occurrence,
'user': usr,
'command': cmd})
# Add header row for parsing
cleandata[:0] = ['minute hour day_of_month month day_of_week user command']
# Add header row for parsing
cleandata[:0] = ['minute hour day_of_month month day_of_week user command']
if len(cleandata) > 1:
cron_list = jc.parsers.universal.simple_table_parse(cleandata)
if len(cleandata) > 1:
cron_list = jc.parsers.universal.simple_table_parse(cleandata)
raw_output['schedule'] = cron_list
raw_output['schedule'] = cron_list
# Add shortcut entries back in
for item in shortcut_list:
raw_output['schedule'].append(item)
# Add shortcut entries back in
for item in shortcut_list:
raw_output['schedule'].append(item)
if raw:
return raw_output

View File

@@ -73,7 +73,7 @@ import jc.parsers.universal
class info():
version = '1.3'
version = '1.4'
description = 'df command parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
@@ -184,14 +184,16 @@ def parse(data, raw=False, quiet=False):
jc.utils.compatibility(__name__, info.compatible)
cleandata = data.splitlines()
raw_output = []
# fix headers
cleandata[0] = cleandata[0].lower()
cleandata[0] = cleandata[0].replace('-', '_')
cleandata[0] = cleandata[0].replace('mounted on', 'mounted_on')
if list(filter(None, cleandata)):
# fix headers
cleandata[0] = cleandata[0].lower()
cleandata[0] = cleandata[0].replace('-', '_')
cleandata[0] = cleandata[0].replace('mounted on', 'mounted_on')
# parse the data
raw_output = jc.parsers.universal.sparse_table_parse(cleandata)
# parse the data
raw_output = jc.parsers.universal.sparse_table_parse(cleandata)
if raw:
return raw_output

View File

@@ -53,7 +53,7 @@ import jc.parsers.universal
class info():
version = '1.0'
version = '1.1'
description = 'free command parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
@@ -122,14 +122,17 @@ def parse(data, raw=False, quiet=False):
jc.utils.compatibility(__name__, info.compatible)
cleandata = data.splitlines()
cleandata[0] = cleandata[0].lower()
cleandata[0] = cleandata[0].replace('buff/cache', 'buff_cache')
cleandata[0] = 'type ' + cleandata[0]
raw_output = []
raw_output = jc.parsers.universal.simple_table_parse(cleandata)
if cleandata:
cleandata[0] = cleandata[0].lower()
cleandata[0] = cleandata[0].replace('buff/cache', 'buff_cache')
cleandata[0] = 'type ' + cleandata[0]
for entry in raw_output:
entry['type'] = entry['type'].rstrip(':')
raw_output = jc.parsers.universal.simple_table_parse(cleandata)
for entry in raw_output:
entry['type'] = entry['type'].rstrip(':')
if raw:
return raw_output

View File

@@ -149,7 +149,7 @@ import jc.utils
class info():
version = '1.4'
version = '1.5'
description = 'ls command parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
@@ -226,20 +226,19 @@ def parse(data, raw=False, quiet=False):
linedata = data.splitlines()
# Delete first line if it starts with 'total 1234'
if linedata:
# Delete first line if it starts with 'total 1234'
if re.match(r'total [0-9]+', linedata[0]):
linedata.pop(0)
# Look for parent line if glob or -R is used
if not re.match(r'[-dclpsbDCMnP?]([-r][-w][-xsS]){2}([-r][-w][-xtT])[+]?', linedata[0]) \
and linedata[0].endswith(':'):
parent = linedata.pop(0)[:-1]
# Pop following total line if it exists
if re.match(r'total [0-9]+', linedata[0]):
linedata.pop(0)
# Look for parent line if glob or -R is used
if not re.match(r'[-dclpsbDCMnP?]([-r][-w][-xsS]){2}([-r][-w][-xtT])[+]?', linedata[0]) \
and linedata[0].endswith(':'):
parent = linedata.pop(0)[:-1]
# Pop following total line if it exists
if re.match(r'total [0-9]+', linedata[0]):
linedata.pop(0)
if linedata:
# Check if -l was used to parse extra data
if re.match(r'[-dclpsbDCMnP?]([-r][-w][-xsS]){2}([-r][-w][-xtT])[+]?', linedata[0]):
for entry in linedata:

View File

@@ -216,7 +216,7 @@ import jc.parsers.universal
class info():
version = '1.3'
version = '1.4'
description = 'lsblk command parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
@@ -330,17 +330,20 @@ def parse(data, raw=False, quiet=False):
linedata = data.splitlines()
# Clear any blank lines
cleandata = list(filter(None, linedata))
cleandata = data.splitlines()
raw_output = []
cleandata[0] = cleandata[0].lower()
cleandata[0] = cleandata[0].replace(':', '_')
cleandata[0] = cleandata[0].replace('-', '_')
if cleandata:
cleandata = data.splitlines()
raw_output = jc.parsers.universal.sparse_table_parse(cleandata)
cleandata[0] = cleandata[0].lower()
cleandata[0] = cleandata[0].replace(':', '_')
cleandata[0] = cleandata[0].replace('-', '_')
# clean up non-ascii characters, if any
for entry in raw_output:
entry['name'] = entry['name'].encode('ascii', errors='ignore').decode()
raw_output = jc.parsers.universal.sparse_table_parse(cleandata)
# clean up non-ascii characters, if any
for entry in raw_output:
entry['name'] = entry['name'].encode('ascii', errors='ignore').decode()
if raw:
return raw_output

View File

@@ -107,7 +107,7 @@ import jc.parsers.universal
class info():
version = '1.1'
version = '1.2'
description = 'lsmod command parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
@@ -175,13 +175,16 @@ def parse(data, raw=False, quiet=False):
jc.utils.compatibility(__name__, info.compatible)
cleandata = data.splitlines()
cleandata[0] = cleandata[0].lower()
raw_output = []
raw_output = jc.parsers.universal.simple_table_parse(cleandata)
if list(filter(None, cleandata)):
cleandata[0] = cleandata[0].lower()
for mod in raw_output:
if 'by' in mod:
mod['by'] = mod['by'].split(',')
raw_output = jc.parsers.universal.simple_table_parse(cleandata)
for mod in raw_output:
if 'by' in mod:
mod['by'] = mod['by'].split(',')
if raw:
return raw_output

View File

@@ -56,7 +56,7 @@ import jc.utils
class info():
version = '1.3'
version = '1.4'
description = 'mount command parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
@@ -162,6 +162,7 @@ def parse(data, raw=False, quiet=False):
# Clear any blank lines
cleandata = list(filter(None, linedata))
raw_output = []
if cleandata:
# check for OSX output

View File

@@ -176,7 +176,11 @@ Examples:
"window": 0,
"irtt": 0,
"iface": "ens33",
"kind": "route"
"kind": "route",
"route_flags_pretty": [
"UP",
"GATEWAY"
]
},
{
"destination": "172.17.0.0",
@@ -187,7 +191,10 @@ Examples:
"window": 0,
"irtt": 0,
"iface": "docker0",
"kind": "route"
"kind": "route",
"route_flags_pretty": [
"UP"
]
},
{
"destination": "192.168.71.0",
@@ -198,7 +205,10 @@ Examples:
"window": 0,
"irtt": 0,
"iface": "ens33",
"kind": "route"
"kind": "route",
"route_flags_pretty": [
"UP"
]
}
]
@@ -237,7 +247,7 @@ Examples:
class info():
version = '1.6'
version = '1.7'
description = 'netstat command parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
@@ -425,25 +435,26 @@ def parse(data, raw=False, quiet=False):
cleandata = list(filter(None, cleandata))
raw_output = []
# check for FreeBSD/OSX vs Linux
# is this from FreeBSD/OSX?
if cleandata[0] == 'Active Internet connections' \
or cleandata[0] == 'Active Internet connections (including servers)' \
or cleandata[0] == 'Active Multipath Internet connections' \
or cleandata[0] == 'Active LOCAL (UNIX) domain sockets' \
or cleandata[0] == 'Registered kernel control modules' \
or cleandata[0] == 'Active kernel event sockets' \
or cleandata[0] == 'Active kernel control sockets' \
or cleandata[0] == 'Routing tables' \
or cleandata[0].startswith('Name '):
if cleandata:
# check for FreeBSD/OSX vs Linux
# is this from FreeBSD/OSX?
if cleandata[0] == 'Active Internet connections' \
or cleandata[0] == 'Active Internet connections (including servers)' \
or cleandata[0] == 'Active Multipath Internet connections' \
or cleandata[0] == 'Active LOCAL (UNIX) domain sockets' \
or cleandata[0] == 'Registered kernel control modules' \
or cleandata[0] == 'Active kernel event sockets' \
or cleandata[0] == 'Active kernel control sockets' \
or cleandata[0] == 'Routing tables' \
or cleandata[0].startswith('Name '):
import jc.parsers.netstat_freebsd_osx
raw_output = jc.parsers.netstat_freebsd_osx.parse(cleandata)
import jc.parsers.netstat_freebsd_osx
raw_output = jc.parsers.netstat_freebsd_osx.parse(cleandata)
# use linux parser
else:
import jc.parsers.netstat_linux
raw_output = jc.parsers.netstat_linux.parse(cleandata)
# use linux parser
else:
import jc.parsers.netstat_linux
raw_output = jc.parsers.netstat_linux.parse(cleandata)
if raw:
return raw_output

View File

@@ -183,7 +183,7 @@ import jc.parsers.universal
class info():
version = '1.1'
version = '1.2'
description = 'ntpq -p command parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
@@ -268,28 +268,29 @@ def parse(data, raw=False, quiet=False):
if not quiet:
jc.utils.compatibility(__name__, info.compatible)
cleandata = data.splitlines()
raw_output = []
cleandata = data.splitlines()
cleandata[0] = 's ' + cleandata[0]
cleandata[0] = cleandata[0].lower()
if list(filter(None, cleandata)):
cleandata[0] = 's ' + cleandata[0]
cleandata[0] = cleandata[0].lower()
# delete header delimiter
del cleandata[1]
# delete header delimiter
del cleandata[1]
# separate first character with a space for easier parsing
for i, line in list(enumerate(cleandata[1:])):
if line[0] == ' ':
# fixup for no-state
cleandata[i + 1] = '~ ' + line[1:]
else:
# fixup - realign columns since we added the 's' column
cleandata[i + 1] = line[:1] + ' ' + line[1:]
# separate first character with a space for easier parsing
for i, line in list(enumerate(cleandata[1:])):
if line[0] == ' ':
# fixup for no-state
cleandata[i + 1] = '~ ' + line[1:]
else:
# fixup - realign columns since we added the 's' column
cleandata[i + 1] = line[:1] + ' ' + line[1:]
# fixup for occaisional ip/hostname fields with a space
cleandata[i + 1] = cleandata[i + 1].replace(' (', '_(')
# fixup for occaisional ip/hostname fields with a space
cleandata[i + 1] = cleandata[i + 1].replace(' (', '_(')
raw_output = jc.parsers.universal.simple_table_parse(cleandata)
raw_output = jc.parsers.universal.simple_table_parse(cleandata)
if raw:
return raw_output

View File

@@ -32,7 +32,7 @@ import jc.parsers.universal
class info():
version = '1.1'
version = '1.2'
description = 'pip list command parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
@@ -93,23 +93,24 @@ def parse(data, raw=False, quiet=False):
# Clear any blank lines
cleandata = list(filter(None, linedata))
# detect legacy output type
if ' (' in cleandata[0]:
for row in cleandata:
raw_output.append({'package': row.split(' (')[0],
'version': row.split(' (')[1].rstrip(')')})
if cleandata:
# detect legacy output type
if ' (' in cleandata[0]:
for row in cleandata:
raw_output.append({'package': row.split(' (')[0],
'version': row.split(' (')[1].rstrip(')')})
# otherwise normal table output
else:
# clear separator line
for i, line in reversed(list(enumerate(cleandata))):
if '---' in line:
cleandata.pop(i)
# otherwise normal table output
else:
# clear separator line
for i, line in reversed(list(enumerate(cleandata))):
if '---' in line:
cleandata.pop(i)
cleandata[0] = cleandata[0].lower()
cleandata[0] = cleandata[0].lower()
if cleandata:
raw_output = jc.parsers.universal.simple_table_parse(cleandata)
if cleandata:
raw_output = jc.parsers.universal.simple_table_parse(cleandata)
if raw:
return raw_output

View File

@@ -177,7 +177,7 @@ import jc.parsers.universal
class info():
version = '1.1'
version = '1.2'
description = 'ps command parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
@@ -282,9 +282,12 @@ def parse(data, raw=False, quiet=False):
jc.utils.compatibility(__name__, info.compatible)
cleandata = data.splitlines()
cleandata[0] = cleandata[0].lower()
raw_output = []
raw_output = jc.parsers.universal.simple_table_parse(cleandata)
if list(filter(None, cleandata)):
cleandata[0] = cleandata[0].lower()
raw_output = jc.parsers.universal.simple_table_parse(cleandata)
if raw:
return raw_output

View File

@@ -14,53 +14,48 @@ Examples:
[
{
"destination": "default",
"gateway": "gateway",
"gateway": "_gateway",
"genmask": "0.0.0.0",
"flags": "UG",
"metric": 100,
"metric": 202,
"ref": 0,
"use": 0,
"iface": "ens33",
"mss": 0,
"window": 0,
"irtt": 0
},
{
"destination": "172.17.0.0",
"gateway": "0.0.0.0",
"genmask": "255.255.0.0",
"flags": "U",
"metric": 0,
"ref": 0,
"use": 0,
"iface": "docker",
"mss": 0,
"window": 0,
"irtt": 0
"irtt": 0,
"flags_pretty": [
"UP",
"GATEWAY"
]
},
{
"destination": "192.168.71.0",
"gateway": "0.0.0.0",
"genmask": "255.255.255.0",
"flags": "U",
"metric": 100,
"metric": 202,
"ref": 0,
"use": 0,
"iface": "ens33",
"mss": 0,
"window": 0,
"irtt": 0
"irtt": 0,
"flags_pretty": [
"UP"
]
}
]
$ route -ee | jc --route -p -r
[
{
"destination": "default",
"gateway": "gateway",
"gateway": "_gateway",
"genmask": "0.0.0.0",
"flags": "UG",
"metric": "100",
"metric": "202",
"ref": "0",
"use": "0",
"iface": "ens33",
@@ -68,25 +63,12 @@ Examples:
"window": "0",
"irtt": "0"
},
{
"destination": "172.17.0.0",
"gateway": "0.0.0.0",
"genmask": "255.255.0.0",
"flags": "U",
"metric": "0",
"ref": "0",
"use": "0",
"iface": "docker",
"mss": "0",
"window": "0",
"irtt": "0"
},
{
"destination": "192.168.71.0",
"gateway": "0.0.0.0",
"genmask": "255.255.255.0",
"flags": "U",
"metric": "100",
"metric": "202",
"ref": "0",
"use": "0",
"iface": "ens33",
@@ -95,13 +77,14 @@ Examples:
"irtt": "0"
}
]
"""
import jc.utils
import jc.parsers.universal
class info():
version = '1.1'
version = '1.2'
description = 'route command parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
@@ -199,9 +182,12 @@ def parse(data, raw=False, quiet=False):
jc.utils.compatibility(__name__, info.compatible)
cleandata = data.splitlines()[1:]
cleandata[0] = cleandata[0].lower()
raw_output = []
raw_output = jc.parsers.universal.simple_table_parse(cleandata)
if list(filter(None, cleandata)):
cleandata[0] = cleandata[0].lower()
raw_output = jc.parsers.universal.simple_table_parse(cleandata)
if raw:
return raw_output

View File

@@ -251,7 +251,7 @@ import jc.utils
class info():
version = '1.0'
version = '1.1'
description = 'ss command parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
@@ -308,17 +308,17 @@ def process(proc_data):
except (ValueError):
entry[key] = None
if 'local_port' in entry:
if 'local_port' in entry:
try:
entry['local_port_num'] = int(entry['local_port'])
except (ValueError):
pass
if 'peer_port' in entry:
try:
entry['peer_port_num'] = int(entry['peer_port'])
except (ValueError):
pass
if 'peer_port' in entry:
try:
entry['peer_port_num'] = int(entry['peer_port'])
except (ValueError):
pass
return proc_data

View File

@@ -40,7 +40,7 @@ import jc.utils
class info():
version = '1.1'
version = '1.2'
description = 'systemctl command parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
@@ -99,24 +99,27 @@ def parse(data, raw=False, quiet=False):
linedata = data.splitlines()
# Clear any blank lines
linedata = list(filter(None, linedata))
# clean up non-ascii characters, if any
cleandata = []
for entry in linedata:
cleandata.append(entry.encode('ascii', errors='ignore').decode())
header_text = cleandata[0]
header_list = header_text.lower().split()
raw_output = []
for entry in cleandata[1:]:
if 'LOAD = ' in entry:
break
if linedata:
# clean up non-ascii characters, if any
cleandata = []
for entry in linedata:
cleandata.append(entry.encode('ascii', errors='ignore').decode())
else:
entry_list = entry.rstrip().split(maxsplit=4)
output_line = dict(zip(header_list, entry_list))
raw_output.append(output_line)
header_text = cleandata[0]
header_list = header_text.lower().split()
raw_output = []
for entry in cleandata[1:]:
if 'LOAD = ' in entry:
break
else:
entry_list = entry.rstrip().split(maxsplit=4)
output_line = dict(zip(header_list, entry_list))
raw_output.append(output_line)
if raw:
return raw_output

View File

@@ -59,7 +59,7 @@ import jc.utils
class info():
version = '1.1'
version = '1.2'
description = 'systemctl list-jobs command parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
@@ -125,25 +125,29 @@ def parse(data, raw=False, quiet=False):
linedata = data.splitlines()
# Clear any blank lines
linedata = list(filter(None, linedata))
# clean up non-ascii characters, if any
cleandata = []
for entry in linedata:
cleandata.append(entry.encode('ascii', errors='ignore').decode())
header_text = cleandata[0]
header_text = header_text.lower()
header_list = header_text.split()
raw_output = []
for entry in cleandata[1:]:
if 'No jobs running.' in entry or 'jobs listed.' in entry:
break
if linedata:
cleandata = []
else:
entry_list = entry.split(maxsplit=4)
output_line = dict(zip(header_list, entry_list))
raw_output.append(output_line)
# clean up non-ascii characters, if any
for entry in linedata:
cleandata.append(entry.encode('ascii', errors='ignore').decode())
header_text = cleandata[0]
header_text = header_text.lower()
header_list = header_text.split()
raw_output = []
for entry in cleandata[1:]:
if 'No jobs running.' in entry or 'jobs listed.' in entry:
break
else:
entry_list = entry.split(maxsplit=4)
output_line = dict(zip(header_list, entry_list))
raw_output.append(output_line)
if raw:
return raw_output

View File

@@ -34,7 +34,7 @@ import jc.utils
class info():
version = '1.1'
version = '1.2'
description = 'systemctl list-sockets command parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
@@ -91,24 +91,27 @@ def parse(data, raw=False, quiet=False):
linedata = data.splitlines()
# Clear any blank lines
linedata = list(filter(None, linedata))
# clean up non-ascii characters, if any
cleandata = []
for entry in linedata:
cleandata.append(entry.encode('ascii', errors='ignore').decode())
header_text = cleandata[0].lower()
header_list = header_text.split()
raw_output = []
for entry in cleandata[1:]:
if 'sockets listed.' in entry:
break
if linedata:
cleandata = []
# clean up non-ascii characters, if any
for entry in linedata:
cleandata.append(entry.encode('ascii', errors='ignore').decode())
else:
entry_list = entry.rsplit(maxsplit=2)
output_line = dict(zip(header_list, entry_list))
raw_output.append(output_line)
header_text = cleandata[0].lower()
header_list = header_text.split()
raw_output = []
for entry in cleandata[1:]:
if 'sockets listed.' in entry:
break
else:
entry_list = entry.rsplit(maxsplit=2)
output_line = dict(zip(header_list, entry_list))
raw_output.append(output_line)
if raw:
return raw_output

View File

@@ -31,7 +31,7 @@ import jc.utils
class info():
version = '1.1'
version = '1.2'
description = 'systemctl list-unit-files command parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
@@ -87,25 +87,28 @@ def parse(data, raw=False, quiet=False):
linedata = data.splitlines()
# Clear any blank lines
linedata = list(filter(None, linedata))
# clean up non-ascii characters, if any
cleandata = []
for entry in linedata:
cleandata.append(entry.encode('ascii', errors='ignore').decode())
header_text = cleandata[0]
header_text = header_text.lower().replace('unit file', 'unit_file')
header_list = header_text.split()
raw_output = []
for entry in cleandata[1:]:
if 'unit files listed.' in entry:
break
if linedata:
cleandata = []
# clean up non-ascii characters, if any
for entry in linedata:
cleandata.append(entry.encode('ascii', errors='ignore').decode())
else:
entry_list = entry.split(maxsplit=4)
output_line = dict(zip(header_list, entry_list))
raw_output.append(output_line)
header_text = cleandata[0]
header_text = header_text.lower().replace('unit file', 'unit_file')
header_list = header_text.split()
raw_output = []
for entry in cleandata[1:]:
if 'unit files listed.' in entry:
break
else:
entry_list = entry.split(maxsplit=4)
output_line = dict(zip(header_list, entry_list))
raw_output.append(output_line)
if raw:
return raw_output

View File

@@ -34,7 +34,7 @@ import jc.utils
class info():
version = '1.0'
version = '1.1'
description = 'uptime command parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
@@ -107,10 +107,9 @@ def parse(data, raw=False, quiet=False):
jc.utils.compatibility(__name__, info.compatible)
raw_output = {}
cleandata = data.splitlines()
if cleandata:
if list(filter(None, cleandata)):
parsed_line = cleandata[0].split()
# allow space for odd times

View File

@@ -83,7 +83,7 @@ import jc.utils
class info():
version = '1.1'
version = '1.2'
description = 'w command parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
@@ -149,36 +149,39 @@ def parse(data, raw=False, quiet=False):
jc.utils.compatibility(__name__, info.compatible)
cleandata = data.splitlines()[1:]
header_text = cleandata[0].lower()
# fixup for 'from' column that can be blank
from_col = header_text.find('from')
# clean up 'login@' header
# even though @ in a key is valid json, it can make things difficult
header_text = header_text.replace('login@', 'login_at')
headers = [h for h in ' '.join(header_text.strip().split()).split() if h]
# parse lines
raw_output = []
if cleandata:
for entry in cleandata[1:]:
output_line = {}
# normalize data by inserting Null for missing data
temp_line = entry.split(maxsplit=len(headers) - 1)
if list(filter(None, cleandata)):
header_text = cleandata[0].lower()
# fixup for 'from' column that can be blank
from_col = header_text.find('from')
# clean up 'login@' header
# even though @ in a key is valid json, it can make things difficult
header_text = header_text.replace('login@', 'login_at')
headers = [h for h in ' '.join(header_text.strip().split()).split() if h]
# fix from column, always at column 2
if 'from' in headers:
if entry[from_col] in string.whitespace:
temp_line.insert(2, '-')
# parse lines
raw_output = []
if cleandata:
for entry in cleandata[1:]:
output_line = {}
output_line = dict(zip(headers, temp_line))
raw_output.append(output_line)
# normalize data by inserting Null for missing data
temp_line = entry.split(maxsplit=len(headers) - 1)
# strip whitespace from beginning and end of all string values
for row in raw_output:
for item in row:
if isinstance(row[item], str):
row[item] = row[item].strip()
# fix from column, always at column 2
if 'from' in headers:
if entry[from_col] in string.whitespace:
temp_line.insert(2, '-')
output_line = dict(zip(headers, temp_line))
raw_output.append(output_line)
# strip whitespace from beginning and end of all string values
for row in raw_output:
for item in row:
if isinstance(row[item], str):
row[item] = row[item].strip()
if raw:
return raw_output

View File

@@ -59,7 +59,7 @@ import xmltodict
class info():
version = '1.0'
version = '1.1'
description = 'XML file parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
@@ -111,7 +111,9 @@ def parse(data, raw=False, quiet=False):
if not quiet:
jc.utils.compatibility(__name__, info.compatible)
if data:
raw_output = []
if list(filter(None, data.splitlines())):
raw_output = xmltodict.parse(data)
if raw:

View File

@@ -5,7 +5,7 @@ with open('README.md', 'r') as f:
setuptools.setup(
name='jc',
version='1.11.2',
version='1.11.5',
author='Kelly Brazil',
author_email='kellyjonbrazil@gmail.com',
description='Converts the output of popular command-line tools and file-types to JSON.',

File diff suppressed because one or more lines are too long

1
tests/fixtures/nixos/route-ee.json vendored Normal file
View File

@@ -0,0 +1 @@
[{"destination": "default", "gateway": "_gateway", "genmask": "0.0.0.0", "flags": "UG", "metric": 202, "ref": 0, "use": 0, "iface": "ens33", "mss": 0, "window": 0, "irtt": 0, "flags_pretty": ["UP", "GATEWAY"]}, {"destination": "192.168.71.0", "gateway": "0.0.0.0", "genmask": "255.255.255.0", "flags": "U", "metric": 202, "ref": 0, "use": 0, "iface": "ens33", "mss": 0, "window": 0, "irtt": 0, "flags_pretty": ["UP"]}]

4
tests/fixtures/nixos/route-ee.out vendored Normal file
View File

@@ -0,0 +1,4 @@
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface MSS Window irtt
default _gateway 0.0.0.0 UG 202 0 0 ens33 0 0 0
192.168.71.0 0.0.0.0 255.255.255.0 U 202 0 0 ens33 0 0 0

File diff suppressed because one or more lines are too long

View File

@@ -17,6 +17,12 @@ class MyTests(unittest.TestCase):
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/osx-10.14.6/airport-I.json'), 'r', encoding='utf-8') as f:
self.osx_10_14_6_airport_I_json = json.loads(f.read())
def test_airport_I_nodata(self):
"""
Test 'airport -I' with no data
"""
self.assertEqual(jc.parsers.airport.parse('', quiet=True), {})
def test_airport_I_osx_10_14_6(self):
"""
Test 'airport -I' on OSX 10.14.6

View File

@@ -17,6 +17,12 @@ class MyTests(unittest.TestCase):
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/osx-10.14.6/airport-s.json'), 'r', encoding='utf-8') as f:
self.osx_10_14_6_airport_s_json = json.loads(f.read())
def test_airport_s_nodata(self):
"""
Test 'airport -s' with no data
"""
self.assertEqual(jc.parsers.airport_s.parse('', quiet=True), [])
def test_airport_s_osx_10_14_6(self):
"""
Test 'airport -s' on OSX 10.14.6

View File

@@ -71,6 +71,12 @@ class MyTests(unittest.TestCase):
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/freebsd12/arp-a.json'), 'r', encoding='utf-8') as f:
self.freebsd12_arp_a_json = json.loads(f.read())
def test_arp_nodata(self):
"""
Test 'arp' with no data
"""
self.assertEqual(jc.parsers.arp.parse('', quiet=True), [])
def test_arp_centos_7_7(self):
"""
Test 'arp' on Centos 7.7

View File

@@ -71,6 +71,12 @@ class MyTests(unittest.TestCase):
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/ubuntu-18.04/blkid-ip-udev-multi.json'), 'r', encoding='utf-8') as f:
self.ubuntu_18_4_blkid_ip_udev_multi_json = json.loads(f.read())
def test_blkid_nodata(self):
"""
Test 'blkid' with no data
"""
self.assertEqual(jc.parsers.blkid.parse('', quiet=True), [])
def test_blkid_centos_7_7(self):
"""
Test 'blkid' on Centos 7.7

View File

@@ -17,6 +17,12 @@ class MyTests(unittest.TestCase):
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/centos-7.7/crontab.json'), 'r', encoding='utf-8') as f:
self.centos_7_7_crontab_json = json.loads(f.read())
def test_crontab_nodata(self):
"""
Test 'crontab' with no data
"""
self.assertEqual(jc.parsers.crontab.parse('', quiet=True), {})
def test_crontab_centos_7_7(self):
"""
Test 'crontab' on Centos 7.7

View File

@@ -23,6 +23,12 @@ class MyTests(unittest.TestCase):
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/centos-7.7/crontab-u.json'), 'r', encoding='utf-8') as f:
self.centos_7_7_crontab_u_json = json.loads(f.read())
def test_crontab_u_nodata(self):
"""
Test 'crontab' with no data (has a user field)
"""
self.assertEqual(jc.parsers.crontab_u.parse('', quiet=True), {})
def test_crontab_u_ubuntu_18_4(self):
"""
Test 'crontab' on Ubuntu 18.4 (has a user field)

View File

@@ -65,6 +65,12 @@ class MyTests(unittest.TestCase):
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/generic/csv-insurance.json'), 'r', encoding='utf-8') as f:
self.generic_csv_insurance_json = json.loads(f.read())
def test_csv_nodata(self):
"""
Test with no data
"""
self.assertEqual(jc.parsers.csv.parse('', quiet=True), [])
def test_csv_biostats(self):
"""
Test 'biostats.csv' file

View File

@@ -59,6 +59,12 @@ class MyTests(unittest.TestCase):
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/osx-10.14.6/df-h.json'), 'r', encoding='utf-8') as f:
self.osx_10_14_6_df_h_json = json.loads(f.read())
def test_df_nodata(self):
"""
Test plain 'df' with no data
"""
self.assertEqual(jc.parsers.df.parse('', quiet=True), [])
def test_df_centos_7_7(self):
"""
Test plain 'df' on Centos 7.7

View File

@@ -101,6 +101,12 @@ class MyTests(unittest.TestCase):
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/osx-10.14.6/dig-axfr.json'), 'r', encoding='utf-8') as f:
self.osx_10_14_6_dig_axfr_json = json.loads(f.read())
def test_dig_nodata(self):
"""
Test 'dig' with no data
"""
self.assertEqual(jc.parsers.dig.parse('', quiet=True), [])
def test_dig_centos_7_7(self):
"""
Test 'dig' on Centos 7.7

View File

@@ -29,6 +29,11 @@ class MyTests(unittest.TestCase):
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/fedora32/dmidecode.json'), 'r', encoding='utf-8') as f:
self.fedora32_dmidecode_json = json.loads(f.read())
def test_dmidecode_nodata(self):
"""
Test 'dmidecode' with no data
"""
self.assertEqual(jc.parsers.dmidecode.parse('', quiet=True), [])
def test_dmidecode_centos_7_7(self):
"""

View File

@@ -35,6 +35,12 @@ class MyTests(unittest.TestCase):
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/osx-10.14.6/du.json'), 'r', encoding='utf-8') as f:
self.osx_10_14_6_du_json = json.loads(f.read())
def test_du_nodata(self):
"""
Test 'du' with no data
"""
self.assertEqual(jc.parsers.du.parse('', quiet=True), [])
def test_du_centos_7_7(self):
"""
Test 'du' on Centos 7.7

View File

@@ -23,6 +23,12 @@ class MyTests(unittest.TestCase):
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/ubuntu-18.04/env.json'), 'r', encoding='utf-8') as f:
self.ubuntu_18_4_env_json = json.loads(f.read())
def test_env_nodata(self):
"""
Test 'env' with no data
"""
self.assertEqual(jc.parsers.env.parse('', quiet=True), [])
def test_env_centos_7_7(self):
"""
Test 'env' on Centos 7.7

View File

@@ -35,6 +35,12 @@ class MyTests(unittest.TestCase):
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/osx-10.14.6/file2.json'), 'r', encoding='utf-8') as f:
self.osx_10_14_6_file2_json = json.loads(f.read())
def test_file_nodata(self):
"""
Test 'file' with no data
"""
self.assertEqual(jc.parsers.file.parse('', quiet=True), [])
def test_file_centos_7_7(self):
"""
Test 'file *' on Centos 7.7

View File

@@ -35,6 +35,12 @@ class MyTests(unittest.TestCase):
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/ubuntu-18.04/free-h.json'), 'r', encoding='utf-8') as f:
self.ubuntu_18_4_free_h_json = json.loads(f.read())
def test_free_nodata(self):
"""
Test 'free' with no data
"""
self.assertEqual(jc.parsers.free.parse('', quiet=True), [])
def test_free_centos_7_7(self):
"""
Test 'free' on Centos 7.7

View File

@@ -23,6 +23,12 @@ class MyTests(unittest.TestCase):
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/ubuntu-18.04/fstab.json'), 'r', encoding='utf-8') as f:
self.ubuntu_18_4_fstab_json = json.loads(f.read())
def test_fstab_nodata(self):
"""
Test 'cat /etc/fstab' with no data
"""
self.assertEqual(jc.parsers.fstab.parse('', quiet=True), [])
def test_fstab_centos_7_7(self):
"""
Test 'cat /etc/fstab' on Centos 7.7

View File

@@ -29,6 +29,12 @@ class MyTests(unittest.TestCase):
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/osx-10.14.6/group.json'), 'r', encoding='utf-8') as f:
self.osx_10_14_6_group_json = json.loads(f.read())
def test_group_nodata(self):
"""
Test 'cat /etc/group' with no data
"""
self.assertEqual(jc.parsers.group.parse('', quiet=True), [])
def test_group_centos_7_7(self):
"""
Test 'cat /etc/group' on Centos 7.7

View File

@@ -23,6 +23,12 @@ class MyTests(unittest.TestCase):
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/ubuntu-18.04/gshadow.json'), 'r', encoding='utf-8') as f:
self.ubuntu_18_4_gshadow_json = json.loads(f.read())
def test_gshadow_nodata(self):
"""
Test 'cat /etc/gshadow' with no data
"""
self.assertEqual(jc.parsers.gshadow.parse('', quiet=True), [])
def test_gshadow_centos_7_7(self):
"""
Test 'cat /etc/gshadow' on Centos 7.7

View File

@@ -23,6 +23,12 @@ class MyTests(unittest.TestCase):
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/ubuntu-18.04/history.json'), 'r', encoding='utf-8') as f:
self.ubuntu_18_4_history_json = json.loads(f.read())
def test_history_nodata(self):
"""
Test 'history' with no data
"""
self.assertEqual(jc.parsers.history.parse('', quiet=True), [])
def test_history_centos_7_7(self):
"""
Test 'history' on Centos 7.7

View File

@@ -23,6 +23,12 @@ class MyTests(unittest.TestCase):
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/ubuntu-18.04/hosts.json'), 'r', encoding='utf-8') as f:
self.ubuntu_18_4_hosts_json = json.loads(f.read())
def test_hosts_nodata(self):
"""
Test 'cat /etc/hosts' with no data
"""
self.assertEqual(jc.parsers.hosts.parse('', quiet=True), [])
def test_hosts_centos_7_7(self):
"""
Test 'cat /etc/hosts' on Centos 7.7

View File

@@ -23,6 +23,12 @@ class MyTests(unittest.TestCase):
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/osx-10.14.6/id.json'), 'r', encoding='utf-8') as f:
self.osx_10_14_6_id_json = json.loads(f.read())
def test_id_nodata(self):
"""
Test 'id' with no data
"""
self.assertEqual(jc.parsers.id.parse('', quiet=True), {})
def test_id_centos_7_7(self):
"""
Test 'id' on Centos 7.7

View File

@@ -47,6 +47,12 @@ class MyTests(unittest.TestCase):
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/osx-10.14.6/ifconfig2.json'), 'r', encoding='utf-8') as f:
self.osx_10_14_6_ifconfig2_json = json.loads(f.read())
def test_ifconfig_nodata(self):
"""
Test 'ifconfig' with no data
"""
self.assertEqual(jc.parsers.ifconfig.parse('', quiet=True), [])
def test_ifconfig_centos_7_7(self):
"""
Test 'ifconfig' on Centos 7.7

View File

@@ -23,6 +23,12 @@ class MyTests(unittest.TestCase):
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/generic/ini-iptelserver.json'), 'r', encoding='utf-8') as f:
self.generic_ini_iptelserver_json = json.loads(f.read())
def test_ini_nodata(self):
"""
Test the test ini file with no data
"""
self.assertEqual(jc.parsers.ini.parse('', quiet=True), {})
def test_ini_test(self):
"""
Test the test ini file

View File

@@ -83,6 +83,12 @@ class MyTests(unittest.TestCase):
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/ubuntu-18.04/iptables-raw.json'), 'r', encoding='utf-8') as f:
self.ubuntu_18_4_iptables_raw_json = json.loads(f.read())
def test_iptables_nodata(self):
"""
Test 'sudo iptables' with no data
"""
self.assertEqual(jc.parsers.iptables.parse('', quiet=True), [])
def test_iptables_filter_centos_7_7(self):
"""
Test 'sudo iptables -L -t filter' on Centos 7.7

View File

@@ -23,6 +23,12 @@ class MyTests(unittest.TestCase):
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/ubuntu-18.04/jobs.json'), 'r', encoding='utf-8') as f:
self.ubuntu_18_4_jobs_json = json.loads(f.read())
def test_jobs_nodata(self):
"""
Test 'jobs' with no data
"""
self.assertEqual(jc.parsers.jobs.parse('', quiet=True), [])
def test_jobs_centos_7_7(self):
"""
Test 'jobs' on Centos 7.7

View File

@@ -65,6 +65,12 @@ class MyTests(unittest.TestCase):
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/freebsd12/last.json'), 'r', encoding='utf-8') as f:
self.freebsd12_last_json = json.loads(f.read())
def test_last_nodata(self):
"""
Test plain 'last' with no data
"""
self.assertEqual(jc.parsers.last.parse('', quiet=True), [])
def test_last_centos_7_7(self):
"""
Test plain 'last' on Centos 7.7

View File

@@ -215,6 +215,12 @@ class MyTests(unittest.TestCase):
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/osx-10.14.6/ls-lR-empty-folder.json'), 'r', encoding='utf-8') as f:
self.osx_10_14_6_ls_lR_empty_folder_json = json.loads(f.read())
def test_ls_empty_dir(self):
"""
Test plain 'ls' on an empty directory
"""
self.assertEqual(jc.parsers.ls.parse('', quiet=True), [])
def test_ls_centos_7_7(self):
"""
Test plain 'ls /' on Centos 7.7

View File

@@ -35,6 +35,12 @@ class MyTests(unittest.TestCase):
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/ubuntu-18.04/lsblk-allcols.json'), 'r', encoding='utf-8') as f:
self.ubuntu_18_4_lsblk_allcols_json = json.loads(f.read())
def test_lsblk_nodata(self):
"""
Test 'lsblk' with no data
"""
self.assertEqual(jc.parsers.lsblk.parse('', quiet=True), [])
def test_lsblk_centos_7_7(self):
"""
Test 'lsblk' on Centos 7.7

View File

@@ -23,6 +23,12 @@ class MyTests(unittest.TestCase):
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/ubuntu-18.04/lsmod.json'), 'r', encoding='utf-8') as f:
self.ubuntu_18_4_lsmod_json = json.loads(f.read())
def test_lsmod_nodata(self):
"""
Test 'lsmod' with no data
"""
self.assertEqual(jc.parsers.lsmod.parse('', quiet=True), [])
def test_lsmod_centos_7_7(self):
"""
Test 'lsmod' on Centos 7.7

View File

@@ -35,6 +35,12 @@ class MyTests(unittest.TestCase):
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/ubuntu-18.04/lsof-sudo.json'), 'r', encoding='utf-8') as f:
self.ubuntu_18_4_lsof_sudo_json = json.loads(f.read())
def test_lsof_nodata(self):
"""
Test 'lsof' with no data
"""
self.assertEqual(jc.parsers.lsof.parse('', quiet=True), [])
def test_lsof_centos_7_7(self):
"""
Test 'lsof' on Centos 7.7

View File

@@ -35,6 +35,12 @@ class MyTests(unittest.TestCase):
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/osx-10.14.6/mount2.json'), 'r', encoding='utf-8') as f:
self.osx_10_14_6_mount2_json = json.loads(f.read())
def test_mount_nodata(self):
"""
Test 'mount' with no data
"""
self.assertEqual(jc.parsers.mount.parse('', quiet=True), [])
def test_mount_centos_7_7(self):
"""
Test 'mount' on Centos 7.7

View File

@@ -227,6 +227,12 @@ class MyTests(unittest.TestCase):
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/freebsd12/netstat-ib.json'), 'r', encoding='utf-8') as f:
self.freebsd12_netstat_ib_json = json.loads(f.read())
def test_netstat_nodata(self):
"""
Test 'netstat' with no data
"""
self.assertEqual(jc.parsers.netstat.parse('', quiet=True), [])
def test_netstat_centos_7_7(self):
"""
Test 'netstat' on Centos 7.7

View File

@@ -47,6 +47,12 @@ class MyTests(unittest.TestCase):
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/freebsd12/ntpq-p.json'), 'r', encoding='utf-8') as f:
self.freebsd12_ntpq_p_json = json.loads(f.read())
def test_ntpq_p_nodata(self):
"""
Test 'ntpq -p' with no data
"""
self.assertEqual(jc.parsers.ntpq.parse('', quiet=True), [])
def test_ntpq_p_centos_7_7(self):
"""
Test 'ntpq -p' on Centos 7.7

View File

@@ -29,6 +29,12 @@ class MyTests(unittest.TestCase):
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/osx-10.14.6/passwd.json'), 'r', encoding='utf-8') as f:
self.osx_10_14_6_passwd_json = json.loads(f.read())
def test_passwd_nodata(self):
"""
Test 'cat /etc/passwd' with no data
"""
self.assertEqual(jc.parsers.passwd.parse('', quiet=True), [])
def test_passwd_centos_7_7(self):
"""
Test 'cat /etc/passwd' on Centos 7.7

View File

@@ -41,6 +41,12 @@ class MyTests(unittest.TestCase):
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/osx-10.14.6/pip-list.json'), 'r', encoding='utf-8') as f:
self.osx_10_14_6_pip_list_json = json.loads(f.read())
def test_pip_list_nodata(self):
"""
Test 'pip_list' with no data
"""
self.assertEqual(jc.parsers.pip_list.parse('', quiet=True), [])
def test_pip_list_centos_7_7(self):
"""
Test 'pip_list' on Centos 7.7

View File

@@ -35,6 +35,12 @@ class MyTests(unittest.TestCase):
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/osx-10.14.6/pip-show.json'), 'r', encoding='utf-8') as f:
self.osx_10_14_6_pip_show_json = json.loads(f.read())
def test_pip_show_nodata(self):
"""
Test 'pip show' with no data
"""
self.assertEqual(jc.parsers.pip_show.parse('', quiet=True), [])
def test_pip_show_centos_7_7(self):
"""
Test 'pip show' on Centos 7.7

View File

@@ -59,6 +59,12 @@ class MyTests(unittest.TestCase):
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/osx-10.14.6/ps-axu.json'), 'r', encoding='utf-8') as f:
self.osx_10_14_6_ps_axu_json = json.loads(f.read())
def test_ps_nodata(self):
"""
Test 'ps' with no data
"""
self.assertEqual(jc.parsers.ps.parse('', quiet=True), [])
def test_ps_ef_centos_7_7(self):
"""
Test 'ps -ef' on Centos 7.7

View File

@@ -22,6 +22,9 @@ class MyTests(unittest.TestCase):
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/ubuntu-18.04/route-vn.out'), 'r', encoding='utf-8') as f:
self.ubuntu_18_4_route_vn = f.read()
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/nixos/route-ee.out'), 'r', encoding='utf-8') as f:
self.nixos_route_ee = f.read()
# output
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/centos-7.7/route.json'), 'r', encoding='utf-8') as f:
self.centos_7_7_route_json = json.loads(f.read())
@@ -35,6 +38,15 @@ class MyTests(unittest.TestCase):
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/ubuntu-18.04/route-vn.json'), 'r', encoding='utf-8') as f:
self.ubuntu_18_4_route_vn_json = json.loads(f.read())
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/nixos/route-ee.json'), 'r', encoding='utf-8') as f:
self.nixos_route_ee_json = json.loads(f.read())
def test_route_nodata(self):
"""
Test 'route' with no data
"""
self.assertEqual(jc.parsers.route.parse('', quiet=True), [])
def test_route_centos_7_7(self):
"""
Test 'route' on Centos 7.7
@@ -59,6 +71,12 @@ class MyTests(unittest.TestCase):
"""
self.assertEqual(jc.parsers.route.parse(self.ubuntu_18_4_route_vn, quiet=True), self.ubuntu_18_4_route_vn_json)
def test_route_ee_nixos(self):
"""
Test 'route -ee' on NixOS
"""
self.assertEqual(jc.parsers.route.parse(self.nixos_route_ee, quiet=True), self.nixos_route_ee_json)
if __name__ == '__main__':
unittest.main()

View File

@@ -23,6 +23,12 @@ class MyTests(unittest.TestCase):
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/ubuntu-18.04/shadow.json'), 'r', encoding='utf-8') as f:
self.ubuntu_18_4_shadow_json = json.loads(f.read())
def test_shadow_nodata(self):
"""
Test 'cat /etc/shadow' with no data
"""
self.assertEqual(jc.parsers.shadow.parse('', quiet=True), [])
def test_shadow_centos_7_7(self):
"""
Test 'cat /etc/shadow' on Centos 7.7

View File

@@ -23,6 +23,12 @@ class MyTests(unittest.TestCase):
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/ubuntu-18.04/ss-sudo-a.json'), 'r', encoding='utf-8') as f:
self.ubuntu_18_4_ss_sudo_a_json = json.loads(f.read())
def test_ss_nodata(self):
"""
Test 'ss' with no data
"""
self.assertEqual(jc.parsers.ss.parse('', quiet=True), [])
def test_ss_sudo_a_centos_7_7(self):
"""
Test 'sudo ss -a' on Centos 7.7

View File

@@ -35,6 +35,12 @@ class MyTests(unittest.TestCase):
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/freebsd12/stat.json'), 'r', encoding='utf-8') as f:
self.freebsd12_stat_json = json.loads(f.read())
def test_stat_nodata(self):
"""
Test 'stat' with no data
"""
self.assertEqual(jc.parsers.stat.parse('', quiet=True), [])
def test_stat_centos_7_7(self):
"""
Test 'stat /bin/*' on Centos 7.7

View File

@@ -23,6 +23,12 @@ class MyTests(unittest.TestCase):
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/ubuntu-18.04/systemctl.json'), 'r', encoding='utf-8') as f:
self.ubuntu_18_4_systemctl_json = json.loads(f.read())
def test_systemctl_nodata(self):
"""
Test 'systemctl' with no data
"""
self.assertEqual(jc.parsers.systemctl.parse('', quiet=True), [])
def test_systemctl_centos_7_7(self):
"""
Test 'systemctl -a' on Centos 7.7

View File

@@ -17,6 +17,12 @@ class MyTests(unittest.TestCase):
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/ubuntu-18.04/systemctl-lj.json'), 'r', encoding='utf-8') as f:
self.ubuntu_18_4_systemctl_lj_json = json.loads(f.read())
def test_systemctl_lj_nodata(self):
"""
Test 'systemctl -a list-jobs' with no data
"""
self.assertEqual(jc.parsers.systemctl_lj.parse('', quiet=True), [])
def test_systemctl_lj_ubuntu_18_4(self):
"""
Test 'systemctl -a list-jobs' on Ubuntu 18.4

View File

@@ -23,6 +23,12 @@ class MyTests(unittest.TestCase):
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/ubuntu-18.04/systemctl-ls.json'), 'r', encoding='utf-8') as f:
self.ubuntu_18_4_systemctl_ls_json = json.loads(f.read())
def test_systemctl_ls_nodata(self):
"""
Test 'systemctl -a list-sockets' with no data
"""
self.assertEqual(jc.parsers.systemctl_ls.parse('', quiet=True), [])
def test_systemctl_ls_centos_7_7(self):
"""
Test 'systemctl -a list-sockets' on Centos 7.7

View File

@@ -23,6 +23,12 @@ class MyTests(unittest.TestCase):
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/ubuntu-18.04/systemctl-luf.json'), 'r', encoding='utf-8') as f:
self.ubuntu_18_4_systemctl_luf_json = json.loads(f.read())
def test_systemctl_luf_nodata(self):
"""
Test 'systemctl -a list-sockets' with no data
"""
self.assertEqual(jc.parsers.systemctl_luf.parse('', quiet=True), [])
def test_systemctl_luf_centos_7_7(self):
"""
Test 'systemctl -a list-sockets' on Centos 7.7

View File

@@ -23,6 +23,12 @@ class MyTests(unittest.TestCase):
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/ubuntu-18.04/timedatectl.json'), 'r', encoding='utf-8') as f:
self.ubuntu_18_4_timedatectl_json = json.loads(f.read())
def test_timedatectl_nodata(self):
"""
Test 'timedatectl' with no data
"""
self.assertEqual(jc.parsers.timedatectl.parse('', quiet=True), {})
def test_timedatectl_centos_7_7(self):
"""
Test 'timedatectl' on Centos 7.7

View File

@@ -35,6 +35,12 @@ class MyTests(unittest.TestCase):
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/osx-10.14.6/uname-a.json'), 'r', encoding='utf-8') as f:
self.osx_10_14_6_uname_a_json = json.loads(f.read())
def test_uname_nodata(self):
"""
Test 'uname -a' with no data
"""
self.assertEqual(jc.parsers.uname.parse('', quiet=True), {})
def test_uname_centos_7_7(self):
"""
Test 'uname -a' on Centos 7.7

View File

@@ -35,6 +35,12 @@ class MyTests(unittest.TestCase):
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/osx-10.14.6/uptime.json'), 'r', encoding='utf-8') as f:
self.osx_10_14_6_uptime_json = json.loads(f.read())
def test_uptime_nodata(self):
"""
Test 'uptime' with no data
"""
self.assertEqual(jc.parsers.uptime.parse('', quiet=True), {})
def test_uptime_centos_7_7(self):
"""
Test 'uptime' on Centos 7.7

View File

@@ -41,6 +41,12 @@ class MyTests(unittest.TestCase):
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/nixos/w.json'), 'r', encoding='utf-8') as f:
self.nixos_w_json = json.loads(f.read())
def test_w_nodata(self):
"""
Test 'w' with no data
"""
self.assertEqual(jc.parsers.w.parse('', quiet=True), [])
def test_w_centos_7_7(self):
"""
Test 'w' on Centos 7.7

View File

@@ -47,6 +47,12 @@ class MyTests(unittest.TestCase):
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/osx-10.14.6/who-a.json'), 'r', encoding='utf-8') as f:
self.osx_10_14_6_who_a_json = json.loads(f.read())
def test_who_nodata(self):
"""
Test 'who' with no data
"""
self.assertEqual(jc.parsers.who.parse('', quiet=True), [])
def test_who_centos_7_7(self):
"""
Test 'who' on Centos 7.7

View File

@@ -23,6 +23,12 @@ class MyTests(unittest.TestCase):
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/generic/xml-foodmenu.json'), 'r', encoding='utf-8') as f:
self.generic_xml_foodmenu_json = json.loads(f.read())
def test_xml_nodata(self):
"""
Test xml parser with no data
"""
self.assertEqual(jc.parsers.xml.parse('', quiet=True), [])
def test_xml_cd_catalog(self):
"""
Test the cd catalog xml file

View File

@@ -23,6 +23,12 @@ class MyTests(unittest.TestCase):
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/generic/yaml-istio-sidecar.json'), 'r', encoding='utf-8') as f:
self.generic_yaml_istio_sidecar_json = json.loads(f.read())
def test_yaml_nodata(self):
"""
Test the YAML parser with no data
"""
self.assertEqual(jc.parsers.yaml.parse('', quiet=True), [])
def test_yaml_istio_sc(self):
"""
Test the Istio SC yaml file