1
0
mirror of https://github.com/kellyjonbrazil/jc.git synced 2026-04-05 17:50:11 +02:00

Compare commits

..

85 Commits

Author SHA1 Message Date
Kelly Brazil
4f8c1a2ca2 version bump 2022-04-27 09:43:55 -07:00
Kelly Brazil
37489dd4e1 doc update 2022-04-27 09:10:38 -07:00
Kelly Brazil
a53aa1d0b8 fix bug when no slaves are present 2022-04-27 09:02:30 -07:00
Kelly Brazil
f7b64a5762 formatting 2022-04-27 08:26:12 -07:00
Kelly Brazil
9b1fe0d9a4 create safe_print_json function 2022-04-27 08:24:54 -07:00
Kelly Brazil
b876645fc5 doc update 2022-04-27 07:38:17 -07:00
Kelly Brazil
d9c0b8f8ad make _safe_print and _asciify private 2022-04-27 07:37:31 -07:00
Kelly Brazil
78d2b239c8 doc update 2022-04-26 17:51:30 -07:00
Kelly Brazil
5a12d98893 update history tests 2022-04-26 17:49:27 -07:00
Kelly Brazil
621f11e6f9 simplify history code 2022-04-26 17:46:16 -07:00
Kelly Brazil
8417f2fe4e use UTF-8 encoding in subprocess.Popen 2022-04-26 15:01:12 -07:00
Kelly Brazil
3ef2a0a065 rename j_data to j_string 2022-04-26 14:29:59 -07:00
Kelly Brazil
faec16d1f2 add line feed 2022-04-26 14:28:18 -07:00
Kelly Brazil
a4ef52b533 refactor try/except blocks to safe_print function 2022-04-26 14:25:34 -07:00
Kelly Brazil
0b407123c2 simplify json_out code 2022-04-26 10:18:49 -07:00
Kelly Brazil
98eedc69ec doc update 2022-04-26 09:57:50 -07:00
Kelly Brazil
dd231ae293 for for UnicodeEncodeError exception when printing JSON 2022-04-26 09:57:12 -07:00
Kelly Brazil
ad6196ddab Merge pull request #236 from kellyjonbrazil/dev
add gentoo to readme
2022-04-26 07:14:46 -07:00
Kelly Brazil
49ca0cecf4 Merge pull request #235 from mgorny/gentoo-install
Add Gentoo Linux install command
2022-04-26 07:10:47 -07:00
Michał Górny
148f2fb6db Add Gentoo Linux install command 2022-04-26 10:38:15 +02:00
Kelly Brazil
e04cd298fb version bump 2022-04-25 19:25:58 -07:00
Kelly Brazil
5bb4e2b4b6 revert f-string to regular string 2022-04-25 19:22:18 -07:00
Kelly Brazil
7b22db50d3 remove single quotes around asciified string 2022-04-25 18:50:36 -07:00
Kelly Brazil
fecb2193ae Merge pull request #234 from kellyjonbrazil/dev
Dev v1.18.7
2022-04-25 17:17:49 -07:00
Kelly Brazil
46af3bd0c0 add UnicodeError fix 2022-04-25 17:04:19 -07:00
Kelly Brazil
c245c89f82 don't try to detect locale, just asciify on exception 2022-04-25 16:51:00 -07:00
Kelly Brazil
a8f0cd9dae C locale fixes 2022-04-25 14:48:59 -07:00
Kelly Brazil
3101189d47 doc update 2022-04-25 08:39:33 -07:00
Kelly Brazil
3f0b644b85 fix test 2022-04-25 08:37:03 -07:00
Kelly Brazil
25c2c483f8 Merge pull request #233 from chriscroome/dev
Update name of alternative from name to alternative
2022-04-25 08:34:34 -07:00
Chris Croome
8bc2ea14a3 Whitespace fix 2022-04-25 11:20:36 +01:00
Chris Croome
8d4f22d9a5 Merge branch 'dev' of github.com:chriscroome/jc into dev 2022-04-25 11:16:47 +01:00
Chris Croome
d2adc2630c Use alternative rather than name to match manpage 2022-04-25 11:16:20 +01:00
Chris Croome
727f2b589e Use alternative rather than name toi match manpage 2022-04-25 11:14:18 +01:00
Kelly Brazil
abbfc92da0 clarify plugin filename requirements 2022-04-22 17:05:22 -07:00
Kelly Brazil
73b7e71cad add tests 2022-04-22 16:25:45 -07:00
Kelly Brazil
4825b16f07 doc update 2022-04-22 15:23:11 -07:00
Kelly Brazil
d2013366cc add update-alternatives examples 2022-04-22 15:20:20 -07:00
Kelly Brazil
96609a2c70 add update-alternatives --get-selections parser 2022-04-22 15:10:48 -07:00
Kelly Brazil
39306573da add update-alternatives parser 2022-04-22 14:47:07 -07:00
Kelly Brazil
01e330aa97 precompile regex patterns 2022-04-22 13:33:36 -07:00
Kelly Brazil
f08a74097d add tests 2022-04-22 12:12:45 -07:00
Kelly Brazil
79e37a7383 fix for oneline output 2022-04-22 12:12:38 -07:00
Kelly Brazil
3eebb6ee19 add update-alternatives parser info 2022-04-22 12:11:56 -07:00
Kelly Brazil
096fffdb79 use stat examples 2022-04-20 10:03:36 -04:00
Kelly Brazil
728d882ed0 add timestamp docs and examples 2022-04-20 09:50:19 -04:00
Kelly Brazil
b53e42aca6 add calculated timestamp 2022-04-20 09:44:42 -04:00
Kelly Brazil
477329ce5b add linefeed to version text 2022-04-20 08:26:26 -04:00
Kelly Brazil
283dc4efd5 add python interpreter version and path to -v and -a output 2022-04-19 17:44:15 -04:00
Kelly Brazil
d490bbcaa0 normalize add/update 2022-04-19 15:26:31 -04:00
Kelly Brazil
f49ddf8e5c doc update 2022-04-19 15:18:04 -04:00
Kelly Brazil
e1e341652b doc update 2022-04-19 15:10:40 -04:00
Kelly Brazil
ecda667549 process integers 2022-04-19 15:10:35 -04:00
Kelly Brazil
a0d96a188a doc update 2022-04-19 14:45:20 -04:00
Kelly Brazil
6c0f0cddfe fix for datetime objects in yaml files 2022-04-19 14:43:47 -04:00
Kelly Brazil
c7173ecd89 fix mypy issues 2022-04-19 14:34:53 -04:00
Kelly Brazil
e98240c905 extend instead of append list 2022-04-19 14:20:44 -04:00
Kelly Brazil
6cb7e25974 add docs 2022-04-19 13:17:24 -04:00
Kelly Brazil
c37980c05c add stat support 2022-04-19 13:17:16 -04:00
Kelly Brazil
b5943bd39d initial docstring 2022-04-19 10:32:55 -04:00
Kelly Brazil
49a3a7db3b initial git-log parser 2022-04-19 09:30:38 -04:00
Kelly Brazil
0c55240e9d move test templates to fix test failures on some systems 2022-04-13 10:54:28 -07:00
Kelly Brazil
f91988aed5 iterator -> iterable 2022-04-12 14:22:06 -07:00
Kelly Brazil
3c3ad9fc6a iterator -> iterable in doc 2022-04-12 14:20:58 -07:00
Kelly Brazil
291ab79e22 fix arch linux command 2022-04-12 11:07:34 -07:00
Kelly Brazil
e6d5892c14 Merge pull request #226 from kellyjonbrazil/master
sync to dev
2022-04-12 11:02:37 -07:00
Kelly Brazil
dcca7a57d5 Merge pull request #225 from adrianteri/patch-1
Update README
2022-04-12 11:00:51 -07:00
003
4ee8a69337 Update README
Specify the Arch package is in the AUR and NOT in the standard Archlinux repositories.
2022-04-12 20:53:39 +03:00
Kelly Brazil
c0414e7db7 clean up background tasks 2022-04-10 17:18:20 -07:00
Kelly Brazil
a419175fe6 enhance parallelization 2022-04-10 17:14:34 -07:00
Kelly Brazil
cd6dead034 man page update 2022-04-10 16:54:34 -07:00
Kelly Brazil
45342ea9fe parallelize doc gneration 2022-04-10 16:54:22 -07:00
Kelly Brazil
585bf0e159 xrandr update 2022-04-10 10:32:51 -07:00
Kelly Brazil
3a860b9bab add rotation field 2022-04-10 10:31:13 -07:00
Kelly Brazil
269180df77 Merge pull request #223 from anekos/fix/support-rotated-devices
Fix/support rotated devices
2022-04-10 10:19:50 -07:00
anekos
a1afed8d95 doc update 2022-04-10 19:53:37 +09:00
anekos
e39f150a21 Support rotated devices 2022-04-10 19:53:37 +09:00
Kelly Brazil
e85f11c6fc doc formatting 2022-04-04 12:00:41 -07:00
Kelly Brazil
49a9d7b07e doc update 2022-04-04 11:42:16 -07:00
Kelly Brazil
a2ef9c429e allow duplicate keys 2022-04-04 11:38:52 -07:00
Kelly Brazil
d5e9074f1c version bump 2022-04-04 11:38:04 -07:00
Kelly Brazil
774699f085 Merge pull request #220 from kellyjonbrazil/master
sync to dev
2022-04-04 18:21:15 +00:00
Kelly Brazil
7138eef3d1 fix newline chars in doc 2022-03-30 14:39:56 -07:00
Kelly Brazil
fad5e544aa formatting 2022-03-29 09:58:44 -07:00
Kelly Brazil
64757e2cf5 doc formatting 2022-03-29 09:35:54 -07:00
84 changed files with 298320 additions and 181 deletions

View File

@@ -1,5 +1,23 @@
jc changelog
20220427 v1.18.8
- Fix update-alternatives --query parser for cases where `slaves` are not present
- Fix UnicodeEncodeError on some systems where LANG=C is set and unicode
characters are in the output
- Update history parser: do not drop non-ASCII characters if the system
is configured for UTF-8 encoding
- Enhance "magic syntax" to always use UTF-8 encoding
20220425 v1.18.7
- Add git log command parser
- Add update-alternatives --query parser
- Add update-alternatives --get-selections parser
- Fix key/value and ini parsers to allow duplicate keys
- Fix yaml file parser for files including timestamp objects
- Update xrandr parser: add a 'rotation' field
- Fix failing tests by moving template files
- Add python interpreter version and path to -v and -a output
20220325 v1.18.6
- Add pidstat command parser tested on linux
- Add pidstat command streaming parser tested on linux
@@ -361,16 +379,16 @@ jc changelog
- Add axfr support for dig command parser
20200312 v1.9.2
- Updated arp parser to fix OSX detection for some edge cases
- Update arp parser to fix OSX detection for some edge cases
20200312 v1.9.1
- Updated file command parser to make filename splitting more robust
- Update file command parser to make filename splitting more robust
20200311 v1.9.0
- Added ntpq command parser
- Added timedatectl status command parser
- Added airport -I and airport -s command parser
- Added file command parser
- Add ntpq command parser
- Add timedatectl status command parser
- Add airport -I and airport -s command parser
- Add file command parser
- Optimized history command parser by https://github.com/philippeitis
- Magic syntax fix for certain edge cases
@@ -378,23 +396,23 @@ jc changelog
- CLI optimizations by https://github.com/philippeitis
- Refactored magic syntax function and added tests (https://github.com/philippeitis)
- Github actions for CI testing on multiple platforms by https://github.com/philippeitis
- Updated ls parser to fix parsing error in OSX with -lR when there are empty folders
- Update ls parser to fix parsing error in OSX with -lR when there are empty folders
20200303 v1.8.0
- Added blkid command parser
- Added last and lastb command parser
- Added who command parser
- Added CSV file parser
- Added /etc/passwd file parser
- Added /etc/shadow file parser
- Added /etc/group file parser
- Added /etc/gshadow file parser
- Add blkid command parser
- Add last and lastb command parser
- Add who command parser
- Add CSV file parser
- Add /etc/passwd file parser
- Add /etc/shadow file parser
- Add /etc/group file parser
- Add /etc/gshadow file parser
20200227 v1.7.5
- Updated ls parser to support filenames with newline characters
- Update ls parser to support filenames with newline characters
20200219 v1.7.4
- Updated ls parser to support multiple directories, globbing, and -R (recursive)
- Update ls parser to support multiple directories, globbing, and -R (recursive)
20200211 v1.7.3
- Add alternative 'magic' syntax: e.g. `jc ls -al`
@@ -411,8 +429,8 @@ jc changelog
- Add crontab file parser with user support (tested on linux)
- Add __version__ variable to parser modules
- Add exit code on error
- Updated history parser to output "line" as an integer
- Updated compatibility list for some parsers
- Update history parser to output "line" as an integer
- Update compatibility list for some parsers
- Bugfix in crontab file parser: header insertion was clobbering first row
- Just-in-time loading of parser modules instead of loading all at start
@@ -425,7 +443,7 @@ jc changelog
- Add tests for ls, dig, ps, w, uptime on OSX
- Add about option
- Add universal parsers to refactor repetitive code
- Updated ifconfig parser to output 'state' as an array
- Update ifconfig parser to output 'state' as an array
20191117 v1.5.1
- Add ss parser
@@ -438,11 +456,11 @@ jc changelog
- Add -d option to debug parsing issues
- Add compatibility warnings to stderr
- Add documentation
- Updated iptables parser to allow --line-numbers option
- Updated lsblk parser to allow parsing of added columns
- Updated mount parser: changed 'access' field name to 'options'
- Updated netstat parser to allow parsing of unix sockets and raw network connections
- Updated w parser to fix unaligned data where blanks are possible
- Update iptables parser to allow --line-numbers option
- Update lsblk parser to allow parsing of added columns
- Update mount parser: changed 'access' field name to 'options'
- Update netstat parser to allow parsing of unix sockets and raw network connections
- Update w parser to fix unaligned data where blanks are possible
- Clean up code and reorganize package
20191031 v1.1.1

View File

@@ -3645,6 +3645,69 @@ uname -a | jc --uname -p # or: jc -p uname -a
"kernel_version": "#74-Ubuntu SMP Tue Sep 17 17:06:04 UTC 2019"
}
```
### update-alternatives --get-selections
```bash
update-alternatives --get-selections | jc --update-alt-gs -p # or: jc -p update-alternatives --get-selections
```
```json
[
{
"name": "arptables",
"status": "auto",
"current": "/usr/sbin/arptables-nft"
},
{
"name": "awk",
"status": "auto",
"current": "/usr/bin/gawk"
}
]
```
### update-alternatives --query
```bash
update-alternatives --query editor | jc --update-alt-q -p # or: jc -p update-alternatives --query editor
```
```json
{
"name": "editor",
"link": "/usr/bin/editor",
"slaves": [
{
"name": "editor.1.gz",
"path": "/usr/share/man/man1/editor.1.gz"
},
{
"name": "editor.da.1.gz",
"path": "/usr/share/man/da/man1/editor.1.gz"
}
],
"status": "auto",
"best": "/bin/nano",
"value": "/bin/nano",
"alternatives": [
{
"name": "/bin/ed",
"priority": -100,
"slaves": [
{
"name": "editor.1.gz",
"path": "/usr/share/man/man1/ed.1.gz"
}
]
},
{
"name": "/bin/nano",
"priority": 40,
"slaves": [
{
"name": "editor.1.gz",
"path": "/usr/share/man/man1/nano.1.gz"
}
]
}
]
}
```
### upower
```bash
upower -i /org/freedesktop/UPower/devices/battery | jc --upower -p # or jc -p upower -i /org/freedesktop/UPower/devices/battery

View File

@@ -106,17 +106,18 @@ pip3 install jc
### OS Package Repositories
| OS | Command |
|-----------------------|-------------------------------------------------------------------------------|
| Debian/Ubuntu linux | `apt-get install jc` |
| Fedora linux | `dnf install jc` |
| openSUSE linux | `zypper install jc` |
| Arch linux | `pacman -S jc` |
| NixOS linux | `nix-env -iA nixpkgs.jc` or `nix-env -iA nixos.jc` |
| Guix System linux | `guix install jc` |
| macOS | `brew install jc` |
| FreeBSD | `portsnap fetch update && cd /usr/ports/textproc/py-jc && make install clean` |
| Ansible filter plugin | `ansible-galaxy collection install community.general` |
| OS | Command |
|--------------------------------------|-------------------------------------------------------------------------------|
| Debian/Ubuntu linux | `apt-get install jc` |
| Fedora linux | `dnf install jc` |
| openSUSE linux | `zypper install jc` |
| Archlinux User Repositories (AUR) | `paru -S jc` or `aura -A jc` or `yay -S jc` |
| NixOS linux | `nix-env -iA nixpkgs.jc` or `nix-env -iA nixos.jc` |
| Guix System linux | `guix install jc` |
| macOS | `brew install jc` |
| FreeBSD | `portsnap fetch update && cd /usr/ports/textproc/py-jc && make install clean` |
| Ansible filter plugin | `ansible-galaxy collection install community.general` |
| Gentoo Linux | `emerge dev-python/jc` |
> For more OS Packages, see https://repology.org/project/jc/versions.
@@ -167,6 +168,7 @@ option.
- `--finger` enables the `finger` command parser ([documentation](https://kellyjonbrazil.github.io/jc/docs/parsers/finger))
- `--free` enables the `free` command parser ([documentation](https://kellyjonbrazil.github.io/jc/docs/parsers/free))
- `--fstab` enables the `/etc/fstab` file parser ([documentation](https://kellyjonbrazil.github.io/jc/docs/parsers/fstab))
- `--git-log` enables the `git log` command parser ([documentation](https://kellyjonbrazil.github.io/jc/docs/parsers/git_log))
- `--group` enables the `/etc/group` file parser ([documentation](https://kellyjonbrazil.github.io/jc/docs/parsers/group))
- `--gshadow` enables the `/etc/gshadow` file parser ([documentation](https://kellyjonbrazil.github.io/jc/docs/parsers/gshadow))
- `--hash` enables the `hash` command parser ([documentation](https://kellyjonbrazil.github.io/jc/docs/parsers/hash))
@@ -227,6 +229,8 @@ option.
- `--ufw` enables the `ufw status` command parser ([documentation](https://kellyjonbrazil.github.io/jc/docs/parsers/ufw))
- `--ufw-appinfo` enables the `ufw app info [application]` command parser ([documentation](https://kellyjonbrazil.github.io/jc/docs/parsers/ufw_appinfo))
- `--uname` enables the `uname -a` command parser ([documentation](https://kellyjonbrazil.github.io/jc/docs/parsers/uname))
- `--update-alt-gs` enables the `update-alternatives --get-selections` command parser ([documentation](https://kellyjonbrazil.github.io/jc/docs/parsers/update_alt_gs))
- `--update-alt-q` enables the `update-alternatives --query` command parser ([documentation](https://kellyjonbrazil.github.io/jc/docs/parsers/update_alt_q))
- `--upower` enables the `upower` command parser ([documentation](https://kellyjonbrazil.github.io/jc/docs/parsers/upower))
- `--uptime` enables the `uptime` command parser ([documentation](https://kellyjonbrazil.github.io/jc/docs/parsers/uptime))
- `--vmstat` enables the `vmstat` command parser ([documentation](https://kellyjonbrazil.github.io/jc/docs/parsers/vmstat))
@@ -399,9 +403,9 @@ Local parser plugins are standard python module files. Use the
or [`jc/parsers/foo_s.py (streaming)`](https://github.com/kellyjonbrazil/jc/blob/master/jc/parsers/foo_s.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 parsers.
Local plugin filenames must be valid python module names and therefore must
start with a letter and consist entirely of alphanumerics. Local plugins
may override default parsers.
> Note: The application data directory follows the
[XDG Base Directory Specification](https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html)
@@ -421,6 +425,9 @@ or by exporting to the environment before running commands:
$ export LANG=C
```
On some older systems UTF-8 output will be downgraded to ASCII with `\\u`
escape sequences if the `C` locale does not support UTF-8 encoding.
#### Timezones
Some parsers have calculated epoch timestamp fields added to the output. Unless

View File

@@ -76,20 +76,30 @@ EOF
)
cd jc
echo Building docs for: package
pydoc-markdown -m jc "${readme_config}" > ../docs/readme.md
(
echo Building docs for: package
pydoc-markdown -m jc "${readme_config}" > ../docs/readme.md; echo "+++ package docs complete"
) &
echo Building docs for: lib
pydoc-markdown -m jc.lib "${toc_config}" > ../docs/lib.md
(
echo Building docs for: lib
pydoc-markdown -m jc.lib "${toc_config}" > ../docs/lib.md; echo "+++ lib docs complete"
) &
echo Building docs for: utils
pydoc-markdown -m jc.utils "${toc_config}" > ../docs/utils.md
(
echo Building docs for: utils
pydoc-markdown -m jc.utils "${toc_config}" > ../docs/utils.md; echo "+++ utils docs complete"
) &
echo Building docs for: streaming
pydoc-markdown -m jc.streaming "${toc_config}" > ../docs/streaming.md
(
echo Building docs for: streaming
pydoc-markdown -m jc.streaming "${toc_config}" > ../docs/streaming.md; echo "+++ streaming docs complete"
) &
echo Building docs for: universal parser
pydoc-markdown -m jc.parsers.universal "${toc_config}" > ../docs/parsers/universal.md
(
echo Building docs for: universal parser
pydoc-markdown -m jc.parsers.universal "${toc_config}" > ../docs/parsers/universal.md; echo "+++ universal parser docs complete"
) &
# a bit of inception here... jc is being used to help
# automate the generation of its own documentation. :)
@@ -103,7 +113,7 @@ do
done < <(jc -a | jq -c '.parsers[] | select(.plugin != true)')
for parser in "${parsers[@]}"
do
do (
parser_name=$(jq -r '.name' <<< "$parser")
compatible=$(jq -r '.compatible | join(", ")' <<< "$parser")
version=$(jq -r '.version' <<< "$parser")
@@ -117,4 +127,8 @@ do
echo "Compatibility: ${compatible}" >> ../docs/parsers/"${parser_name}".md
echo >> ../docs/parsers/"${parser_name}".md
echo "Version ${version} by ${author} (${author_email})" >> ../docs/parsers/"${parser_name}".md
echo "+++ ${parser_name} docs complete"
) &
done
wait
echo "Document Generation Complete"

View File

@@ -68,7 +68,7 @@ Parameters:
variants of the module name.
data: (string or data to parse (string for normal
iterator) parsers, iterator of strings for
iterable) parsers, iterable of strings for
streaming parsers)
raw: (boolean) output preprocessed JSON if True

View File

@@ -20,7 +20,7 @@ For example:
│ hi there │ abc def │ 3.14 │
╘══════════╧═════════╧════════╛
or
or
+-----------------------------+
| foo bar baz |
@@ -29,30 +29,30 @@ For example:
| hi there abc def 3.14 |
+-----------------------------+
or
or
| foo | bar | baz |
|----------|---------|--------|
| good day | | 12345 |
| hi there | abc def | 3.14 |
or
or
foo bar baz
--------- -------- ------
good day 12345
hi there abc def 3.14
or
or
foo bar baz
good day 12345
hi there abc def 3.14
etc...
etc...
Headers (keys) are converted to snake-case. All values are returned as
strings, except empty strings, which are converted to None/null.
Headers (keys) are converted to snake-case. All values are returned as
strings, except empty strings, which are converted to None/null.
Usage (cli):

View File

@@ -23,7 +23,7 @@ For example:
╘══════════╧═════════╧════════╛
Cells with multiple lines within rows will be joined with a newline
character ('\n').
character ('\\n').
Headers (keys) are converted to snake-case and newlines between multi-line
headers are joined with an underscore. All values are returned as strings,
@@ -62,7 +62,7 @@ Examples:
> +==========+=========+========+' | jc --asciitable-m -p
[
{
"foo": "good day\nmate",
"foo": "good day\\nmate",
"bar": "12345",
"baz_buz": null
},
@@ -86,7 +86,7 @@ Examples:
> ╘══════════╧═════════╧════════╛' | jc --asciitable-m -p
[
{
"foo": "good day\nmate",
"foo": "good day\\nmate",
"bar": "12345",
"baz_buz": null
},

175
docs/parsers/git_log.md Normal file
View File

@@ -0,0 +1,175 @@
[Home](https://kellyjonbrazil.github.io/jc/)
<a id="jc.parsers.git_log"></a>
# jc.parsers.git\_log
jc - JSON Convert `git log` command output parser
Can be used with the following format options:
- `oneline`
- `short`
- `medium`
- `full`
- `fuller`
Additional options supported:
- `--stat`
- `--shortstat`
The `epoch` calculated timestamp field is naive. (i.e. based on the
local time of the system the parser is run on)
The `epoch_utc` calculated timestamp field is timezone-aware and is
only available if the timezone field is UTC.
Usage (cli):
$ git log | jc --git-log
or
$ jc git log
Usage (module):
import jc
result = jc.parse('git_log', git_log_command_output)
Schema:
[
{
"commit": string,
"author": string,
"author_email": string,
"date": string,
"epoch": integer, [0]
"epoch_utc": integer, [1]
"commit_by": string,
"commit_by_email": string,
"commit_by_date": string,
"message": string,
"stats" : {
"files_changed": integer,
"insertions": integer,
"deletions": integer,
"files": [
string
]
}
}
]
[0] naive timestamp if "date" field is parsable, else null
[1] timezone aware timestamp availabe for UTC, else null
Examples:
$ git log --stat | jc --git-log -p
[
{
"commit": "728d882ed007b3c8b785018874a0eb06e1143b66",
"author": "Kelly Brazil",
"author_email": "kellyjonbrazil@gmail.com",
"date": "Wed Apr 20 09:50:19 2022 -0400",
"stats": {
"files_changed": 2,
"insertions": 90,
"deletions": 12,
"files": [
"docs/parsers/git_log.md",
"jc/parsers/git_log.py"
]
},
"message": "add timestamp docs and examples",
"epoch": 1650462619,
"epoch_utc": null
},
{
"commit": "b53e42aca623181aa9bc72194e6eeef1e9a3a237",
"author": "Kelly Brazil",
"author_email": "kellyjonbrazil@gmail.com",
"date": "Wed Apr 20 09:44:42 2022 -0400",
"stats": {
"files_changed": 5,
"insertions": 29,
"deletions": 6,
"files": [
"docs/parsers/git_log.md",
"docs/utils.md",
"jc/parsers/git_log.py",
"jc/utils.py",
"man/jc.1"
]
},
"message": "add calculated timestamp",
"epoch": 1650462282,
"epoch_utc": null
},
...
]
$ git log --stat | jc --git-log -p -r
[
{
"commit": "728d882ed007b3c8b785018874a0eb06e1143b66",
"author": "Kelly Brazil",
"author_email": "kellyjonbrazil@gmail.com",
"date": "Wed Apr 20 09:50:19 2022 -0400",
"stats": {
"files_changed": "2",
"insertions": "90",
"deletions": "12",
"files": [
"docs/parsers/git_log.md",
"jc/parsers/git_log.py"
]
},
"message": "add timestamp docs and examples"
},
{
"commit": "b53e42aca623181aa9bc72194e6eeef1e9a3a237",
"author": "Kelly Brazil",
"author_email": "kellyjonbrazil@gmail.com",
"date": "Wed Apr 20 09:44:42 2022 -0400",
"stats": {
"files_changed": "5",
"insertions": "29",
"deletions": "6",
"files": [
"docs/parsers/git_log.md",
"docs/utils.md",
"jc/parsers/git_log.py",
"jc/utils.py",
"man/jc.1"
]
},
"message": "add calculated timestamp"
},
...
]
<a id="jc.parsers.git_log.parse"></a>
### parse
```python
def parse(data: str, raw: bool = False, quiet: bool = False) -> List[Dict]
```
Main text parsing function
Parameters:
data: (string) text data to parse
raw: (boolean) unprocessed output if True
quiet: (boolean) suppress warning messages if True
Returns:
List of Dictionaries. Raw or processed structured data.
### Parser Information
Compatibility: linux, darwin, cygwin, win32, aix, freebsd
Version 1.0 by Kelly Brazil (kellyjonbrazil@gmail.com)

View File

@@ -87,4 +87,4 @@ Returns:
### Parser Information
Compatibility: linux, darwin, cygwin, aix, freebsd
Version 1.6 by Kelly Brazil (kellyjonbrazil@gmail.com)
Version 1.7 by Kelly Brazil (kellyjonbrazil@gmail.com)

View File

@@ -6,12 +6,14 @@
jc - JSON Convert `INI` file parser
Parses standard `INI` files and files containing simple key/value pairs.
Delimiter can be `=` or `:`. Missing values are supported. Comment prefix
can be `#` or `;`. Comments must be on their own line.
Note: Values starting and ending with quotation marks will have the marks
removed. If you would like to keep the quotation marks, use the `-r`
command-line argument or the `raw=True` argument in `parse()`.
- Delimiter can be `=` or `:`. Missing values are supported.
- Comment prefix can be `#` or `;`. Comments must be on their own line.
- If duplicate keys are found, only the last value will be used.
> Note: Values starting and ending with quotation marks will have the marks
removed. If you would like to keep the quotation marks, use the `-r`
command-line argument or the `raw=True` argument in `parse()`.
Usage (cli):
@@ -89,4 +91,4 @@ Returns:
### Parser Information
Compatibility: linux, darwin, cygwin, win32, aix, freebsd
Version 1.5 by Kelly Brazil (kellyjonbrazil@gmail.com)
Version 1.6 by Kelly Brazil (kellyjonbrazil@gmail.com)

View File

@@ -5,13 +5,15 @@
jc - JSON Convert `Key/Value` file parser
Supports files containing simple key/value pairs. Delimiter can be `=` or
`:`. Missing values are supported. Comment prefix can be `#` or `;`.
Comments must be on their own line.
Supports files containing simple key/value pairs.
Note: Values starting and ending with quotation marks will have the marks
removed. If you would like to keep the quotation marks, use the `-r`
command-line argument or the `raw=True` argument in `parse()`.
- Delimiter can be `=` or `:`. Missing values are supported.
- Comment prefix can be `#` or `;`. Comments must be on their own line.
- If duplicate keys are found, only the last value will be used.
> Note: Values starting and ending with quotation marks will have the marks
removed. If you would like to keep the quotation marks, use the `-r`
command-line argument or the `raw=True` argument in `parse()`.
Usage (cli):
@@ -78,4 +80,4 @@ Returns:
### Parser Information
Compatibility: linux, darwin, cygwin, win32, aix, freebsd
Version 1.1 by Kelly Brazil (kellyjonbrazil@gmail.com)
Version 1.2 by Kelly Brazil (kellyjonbrazil@gmail.com)

View File

@@ -0,0 +1,71 @@
[Home](https://kellyjonbrazil.github.io/jc/)
<a id="jc.parsers.update_alt_gs"></a>
# jc.parsers.update\_alt\_gs
jc - JSON Convert `update-alternatives --get-selections` command output parser
Usage (cli):
$ update-alternatives --get-selections | jc --update-alt-gs
or
$ jc update-alternatives --get-selections
Usage (module):
import jc
result = jc.parse('update-alt-gs',
update_alternatives_get_selections_command_output)
Schema:
[
{
"name": string,
"status": string,
"current": string
}
]
Examples:
$ update-alternatives --get-selections | jc --update-alt-gs -p
[
{
"name": "arptables",
"status": "auto",
"current": "/usr/sbin/arptables-nft"
},
{
"name": "awk",
"status": "auto",
"current": "/usr/bin/gawk"
}
]
<a id="jc.parsers.update_alt_gs.parse"></a>
### parse
```python
def parse(data: str, raw: bool = False, quiet: bool = False) -> List[Dict]
```
Main text parsing function
Parameters:
data: (string) text data to parse
raw: (boolean) unprocessed output if True
quiet: (boolean) suppress warning messages if True
Returns:
List of Dictionaries. Raw or processed structured data.
### Parser Information
Compatibility: linux
Version 1.0 by Kelly Brazil (kellyjonbrazil@gmail.com)

View File

@@ -0,0 +1,157 @@
[Home](https://kellyjonbrazil.github.io/jc/)
<a id="jc.parsers.update_alt_q"></a>
# jc.parsers.update\_alt\_q
jc - JSON Convert `update-alternatives --query` command output parser
Usage (cli):
$ update-alternatives --query | jc --update-alt-q
or
$ jc update-alternatives --query
Usage (module):
import jc
result = jc.parse('update_alt_q',
update_alternatives_query_command_output)
Schema:
{
"name": string,
"link": string,
"slaves": [
{
"name": string,
"path": string
}
],
"status": string,
"best": string,
"value": string, # (null if 'none')
"alternatives": [
{
"alternative": string,
"priority": integer,
"slaves": [
{
"name": string,
"path": string
}
]
}
]
}
Examples:
$ update-alternatives --query editor | jc --update-alt-q -p
{
"name": "editor",
"link": "/usr/bin/editor",
"slaves": [
{
"name": "editor.1.gz",
"path": "/usr/share/man/man1/editor.1.gz"
},
{
"name": "editor.da.1.gz",
"path": "/usr/share/man/da/man1/editor.1.gz"
}
],
"status": "auto",
"best": "/bin/nano",
"value": "/bin/nano",
"alternatives": [
{
"alternative": "/bin/ed",
"priority": -100,
"slaves": [
{
"name": "editor.1.gz",
"path": "/usr/share/man/man1/ed.1.gz"
}
]
},
{
"alternative": "/bin/nano",
"priority": 40,
"slaves": [
{
"name": "editor.1.gz",
"path": "/usr/share/man/man1/nano.1.gz"
}
]
}
]
}
$ update-alternatives --query | jc --update-alt-q -p -r
{
"name": "editor",
"link": "/usr/bin/editor",
"slaves": [
{
"name": "editor.1.gz",
"path": "/usr/share/man/man1/editor.1.gz"
},
{
"name": "editor.da.1.gz",
"path": "/usr/share/man/da/man1/editor.1.gz"
}
],
"status": "auto",
"best": "/bin/nano",
"value": "/bin/nano",
"alternatives": [
{
"alternative": "/bin/ed",
"priority": "-100",
"slaves": [
{
"name": "editor.1.gz",
"path": "/usr/share/man/man1/ed.1.gz"
}
]
},
{
"alternative": "/bin/nano",
"priority": "40",
"slaves": [
{
"name": "editor.1.gz",
"path": "/usr/share/man/man1/nano.1.gz"
}
]
}
]
}
<a id="jc.parsers.update_alt_q.parse"></a>
### parse
```python
def parse(data: str, raw: bool = False, quiet: bool = False) -> Dict
```
Main text parsing function
Parameters:
data: (string) text data to parse
raw: (boolean) unprocessed output if True
quiet: (boolean) suppress warning messages if True
Returns:
Dictionary. Raw or processed structured data.
### Parser Information
Compatibility: linux
Version 1.1 by Kelly Brazil (kellyjonbrazil@gmail.com)

View File

@@ -54,7 +54,8 @@ Schema:
"offset_width": integer,
"offset_height": integer,
"dimension_width": integer,
"dimension_height": integer
"dimension_height": integer,
"rotation": string
}
],
"unassociated_devices": [
@@ -130,7 +131,8 @@ Examples:
"offset_width": 0,
"offset_height": 0,
"dimension_width": 310,
"dimension_height": 170
"dimension_height": 170,
"rotation": "normal"
}
}
],
@@ -160,4 +162,4 @@ Returns:
### Parser Information
Compatibility: linux, darwin, cygwin, aix, freebsd
Version 1.0 by Kevin Lyter (lyter_git at sent.com)
Version 1.1 by Kevin Lyter (lyter_git at sent.com)

View File

@@ -5,6 +5,8 @@
jc - JSON Convert `YAML` file parser
Note: datetime objects will be converted to strings.
Usage (cli):
$ cat foo.yaml | jc --yaml
@@ -107,4 +109,4 @@ Returns:
### Parser Information
Compatibility: linux, darwin, cygwin, win32, aix, freebsd
Version 1.6 by Kelly Brazil (kellyjonbrazil@gmail.com)
Version 1.7 by Kelly Brazil (kellyjonbrazil@gmail.com)

View File

@@ -187,7 +187,7 @@ class timestamp()
```python
def __init__(datetime_string: str,
format_hint: Union[List, Tuple, None] = None) -> None
format_hint: Optional[Iterable] = None) -> None
```
Input a datetime text string of several formats and convert to a
@@ -198,7 +198,7 @@ Parameters:
datetime_string (str): a string representation of a
datetime in several supported formats
format_hint (list | tuple): an optional list of format ID
format_hint (iterable): an optional iterable of format ID
integers to instruct the timestamp object to try those
formats first in the order given. Other formats will be
tried after the format hint list is exhausted. This can

View File

@@ -84,6 +84,26 @@ if PYGMENTS_INSTALLED:
}
def safe_print_json(string, pretty=None, env_colors=None, mono=None,
piped_out=None, flush=None):
"""Safely prints JSON output in both UTF-8 and ASCII systems"""
try:
print(json_out(string,
pretty=pretty,
env_colors=env_colors,
mono=mono,
piped_out=piped_out),
flush=flush)
except UnicodeEncodeError:
print(json_out(string,
pretty=pretty,
env_colors=env_colors,
mono=mono,
piped_out=piped_out,
ascii_only=True),
flush=flush)
def set_env_colors(env_colors=None):
"""
Return a dictionary to be used in Pygments custom style class.
@@ -177,6 +197,8 @@ def about_jc():
'website': info.website,
'copyright': info.copyright,
'license': info.license,
'python_version': '.'.join((str(sys.version_info.major), str(sys.version_info.minor), str(sys.version_info.micro))),
'python_path': sys.executable,
'parser_count': len(all_parser_info()),
'parsers': all_parser_info()
}
@@ -249,14 +271,19 @@ def help_doc(options):
def versiontext():
"""Return the version text"""
py_ver = '.'.join((str(sys.version_info.major), str(sys.version_info.minor), str(sys.version_info.micro)))
versiontext_string = f'''\
jc version {info.version}
jc version: {info.version}
python interpreter version: {py_ver}
python path: {sys.executable}
{info.website}
{info.copyright}'''
{info.copyright}
'''
return textwrap.dedent(versiontext_string)
def json_out(data, pretty=False, env_colors=None, mono=False, piped_out=False):
def json_out(data, pretty=False, env_colors=None, mono=False, piped_out=False, ascii_only=False):
"""
Return a JSON formatted string. String may include color codes or be
pretty printed.
@@ -268,15 +295,16 @@ def json_out(data, pretty=False, env_colors=None, mono=False, piped_out=False):
separators = None
indent = 2
j_string = json.dumps(data, indent=indent, separators=separators, ensure_ascii=ascii_only)
if not mono and not piped_out:
# set colors
class JcStyle(Style):
styles = set_env_colors(env_colors)
return str(highlight(json.dumps(data, indent=indent, separators=separators, ensure_ascii=False),
JsonLexer(), Terminal256Formatter(style=JcStyle))[0:-1])
return str(highlight(j_string, JsonLexer(), Terminal256Formatter(style=JcStyle))[0:-1])
return json.dumps(data, indent=indent, separators=separators, ensure_ascii=False)
return j_string
def magic_parser(args):
@@ -347,7 +375,8 @@ def run_user_command(command):
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
close_fds=False, # Allows inheriting file descriptors;
universal_newlines=True) # useful for process substitution
universal_newlines=True, # useful for process substitution
encoding='UTF-8')
stdout, stderr = proc.communicate()
return (
@@ -414,19 +443,19 @@ def main():
mono = True
if about:
print(json_out(about_jc(),
pretty=pretty,
env_colors=jc_colors,
mono=mono,
piped_out=piped_output(force_color)))
safe_print_json(about_jc(),
pretty=pretty,
env_colors=jc_colors,
mono=mono,
piped_out=piped_output(force_color))
sys.exit(0)
if help_me:
print(help_doc(sys.argv))
utils._safe_print(help_doc(sys.argv))
sys.exit(0)
if version_info:
print(versiontext())
utils._safe_print(versiontext())
sys.exit(0)
# if magic syntax used, try to run the command and error if it's not found, etc.
@@ -441,7 +470,7 @@ def main():
try:
magic_stdout, magic_stderr, magic_exit_code = run_user_command(run_command)
if magic_stderr:
print(magic_stderr[:-1], file=sys.stderr)
utils._safe_print(magic_stderr[:-1], file=sys.stderr)
except OSError as e:
if debug:
@@ -505,12 +534,12 @@ def main():
quiet=quiet,
ignore_exceptions=ignore_exceptions)
for line in result:
print(json_out(line,
pretty=pretty,
env_colors=jc_colors,
mono=mono,
piped_out=piped_output(force_color)),
flush=unbuffer)
safe_print_json(line,
pretty=pretty,
env_colors=jc_colors,
mono=mono,
piped_out=piped_output(force_color),
flush=unbuffer)
sys.exit(combined_exit_code(magic_exit_code, 0))
@@ -520,12 +549,13 @@ def main():
result = parser.parse(data,
raw=raw,
quiet=quiet)
print(json_out(result,
pretty=pretty,
env_colors=jc_colors,
mono=mono,
piped_out=piped_output(force_color)),
flush=unbuffer)
safe_print_json(result,
pretty=pretty,
env_colors=jc_colors,
mono=mono,
piped_out=piped_output(force_color),
flush=unbuffer)
sys.exit(combined_exit_code(magic_exit_code, 0))

View File

@@ -6,7 +6,7 @@ import importlib
from typing import Dict, List, Iterable, Union, Iterator
from jc import appdirs
__version__ = '1.18.6'
__version__ = '1.18.8'
parsers = [
'acpi',
@@ -33,6 +33,7 @@ parsers = [
'finger',
'free',
'fstab',
'git-log',
'group',
'gshadow',
'hash',
@@ -93,6 +94,8 @@ parsers = [
'ufw',
'ufw-appinfo',
'uname',
'update-alt-gs',
'update-alt-q',
'upower',
'uptime',
'vmstat',
@@ -206,7 +209,7 @@ def parse(
variants of the module name.
data: (string or data to parse (string for normal
iterator) parsers, iterator of strings for
iterable) parsers, iterable of strings for
streaming parsers)
raw: (boolean) output preprocessed JSON if True

View File

@@ -15,7 +15,7 @@ For example:
│ hi there │ abc def │ 3.14 │
╘══════════╧═════════╧════════╛
or
or
+-----------------------------+
| foo bar baz |
@@ -24,30 +24,30 @@ For example:
| hi there abc def 3.14 |
+-----------------------------+
or
or
| foo | bar | baz |
|----------|---------|--------|
| good day | | 12345 |
| hi there | abc def | 3.14 |
or
or
foo bar baz
--------- -------- ------
good day 12345
hi there abc def 3.14
or
or
foo bar baz
good day 12345
hi there abc def 3.14
etc...
etc...
Headers (keys) are converted to snake-case. All values are returned as
strings, except empty strings, which are converted to None/null.
Headers (keys) are converted to snake-case. All values are returned as
strings, except empty strings, which are converted to None/null.
Usage (cli):

View File

@@ -18,7 +18,7 @@ For example:
╘══════════╧═════════╧════════╛
Cells with multiple lines within rows will be joined with a newline
character ('\n').
character ('\\n').
Headers (keys) are converted to snake-case and newlines between multi-line
headers are joined with an underscore. All values are returned as strings,
@@ -57,7 +57,7 @@ Examples:
> +==========+=========+========+' | jc --asciitable-m -p
[
{
"foo": "good day\nmate",
"foo": "good day\\nmate",
"bar": "12345",
"baz_buz": null
},
@@ -81,7 +81,7 @@ Examples:
> ╘══════════╧═════════╧════════╛' | jc --asciitable-m -p
[
{
"foo": "good day\nmate",
"foo": "good day\\nmate",
"bar": "12345",
"baz_buz": null
},

329
jc/parsers/git_log.py Normal file
View File

@@ -0,0 +1,329 @@
"""jc - JSON Convert `git log` command output parser
Can be used with the following format options:
- `oneline`
- `short`
- `medium`
- `full`
- `fuller`
Additional options supported:
- `--stat`
- `--shortstat`
The `epoch` calculated timestamp field is naive. (i.e. based on the
local time of the system the parser is run on)
The `epoch_utc` calculated timestamp field is timezone-aware and is
only available if the timezone field is UTC.
Usage (cli):
$ git log | jc --git-log
or
$ jc git log
Usage (module):
import jc
result = jc.parse('git_log', git_log_command_output)
Schema:
[
{
"commit": string,
"author": string,
"author_email": string,
"date": string,
"epoch": integer, [0]
"epoch_utc": integer, [1]
"commit_by": string,
"commit_by_email": string,
"commit_by_date": string,
"message": string,
"stats" : {
"files_changed": integer,
"insertions": integer,
"deletions": integer,
"files": [
string
]
}
}
]
[0] naive timestamp if "date" field is parsable, else null
[1] timezone aware timestamp availabe for UTC, else null
Examples:
$ git log --stat | jc --git-log -p
[
{
"commit": "728d882ed007b3c8b785018874a0eb06e1143b66",
"author": "Kelly Brazil",
"author_email": "kellyjonbrazil@gmail.com",
"date": "Wed Apr 20 09:50:19 2022 -0400",
"stats": {
"files_changed": 2,
"insertions": 90,
"deletions": 12,
"files": [
"docs/parsers/git_log.md",
"jc/parsers/git_log.py"
]
},
"message": "add timestamp docs and examples",
"epoch": 1650462619,
"epoch_utc": null
},
{
"commit": "b53e42aca623181aa9bc72194e6eeef1e9a3a237",
"author": "Kelly Brazil",
"author_email": "kellyjonbrazil@gmail.com",
"date": "Wed Apr 20 09:44:42 2022 -0400",
"stats": {
"files_changed": 5,
"insertions": 29,
"deletions": 6,
"files": [
"docs/parsers/git_log.md",
"docs/utils.md",
"jc/parsers/git_log.py",
"jc/utils.py",
"man/jc.1"
]
},
"message": "add calculated timestamp",
"epoch": 1650462282,
"epoch_utc": null
},
...
]
$ git log --stat | jc --git-log -p -r
[
{
"commit": "728d882ed007b3c8b785018874a0eb06e1143b66",
"author": "Kelly Brazil",
"author_email": "kellyjonbrazil@gmail.com",
"date": "Wed Apr 20 09:50:19 2022 -0400",
"stats": {
"files_changed": "2",
"insertions": "90",
"deletions": "12",
"files": [
"docs/parsers/git_log.md",
"jc/parsers/git_log.py"
]
},
"message": "add timestamp docs and examples"
},
{
"commit": "b53e42aca623181aa9bc72194e6eeef1e9a3a237",
"author": "Kelly Brazil",
"author_email": "kellyjonbrazil@gmail.com",
"date": "Wed Apr 20 09:44:42 2022 -0400",
"stats": {
"files_changed": "5",
"insertions": "29",
"deletions": "6",
"files": [
"docs/parsers/git_log.md",
"docs/utils.md",
"jc/parsers/git_log.py",
"jc/utils.py",
"man/jc.1"
]
},
"message": "add calculated timestamp"
},
...
]
"""
import re
from typing import List, Dict
import jc.utils
hash_pattern = re.compile(r'([0-9]|[a-f])+')
changes_pattern = re.compile(r'\s(?P<files>\d+)\s+(files? changed),\s+(?P<insertions>\d+)\s(insertions?\(\+\))?(,\s+)?(?P<deletions>\d+)?(\s+deletions?\(\-\))?')
class info():
"""Provides parser metadata (version, author, etc.)"""
version = '1.0'
description = '`git log` command parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
compatible = ['linux', 'darwin', 'cygwin', 'win32', 'aix', 'freebsd']
magic_commands = ['git log']
__version__ = info.version
def _process(proc_data: List[Dict]) -> List[Dict]:
"""
Final processing to conform to the schema.
Parameters:
proc_data: (List of Dictionaries) raw structured data to process
Returns:
List of Dictionaries. Structured to conform to the schema.
"""
int_list = ['files_changed', 'insertions', 'deletions']
for entry in proc_data:
if 'date' in entry:
ts = jc.utils.timestamp(entry['date'], format_hint=(1100,))
entry['epoch'] = ts.naive
entry['epoch_utc'] = ts.utc
if 'stats' in entry:
for key in entry['stats']:
if key in int_list:
entry['stats'][key] = jc.utils.convert_to_int(entry['stats'][key])
return proc_data
def _is_commit_hash(hash_string: str) -> bool:
# 0c55240e9da30ac4293dc324f1094de2abd3da91
if len(hash_string) != 40:
return False
if hash_pattern.match(hash_string):
return True
return False
def parse(
data: str,
raw: bool = False,
quiet: bool = False
) -> List[Dict]:
"""
Main text parsing function
Parameters:
data: (string) text data to parse
raw: (boolean) unprocessed output if True
quiet: (boolean) suppress warning messages if True
Returns:
List of Dictionaries. Raw or processed structured data.
"""
jc.utils.compatibility(__name__, info.compatible, quiet)
jc.utils.input_type_check(data)
raw_output: List = []
output_line: Dict = {}
message_lines: List[str] = []
file_list: List[str] = []
if jc.utils.has_data(data):
for line in data.splitlines():
line_list = line.split(maxsplit=1)
# oneline style
if line_list and _is_commit_hash(line_list[0]):
if output_line:
if file_list:
output_line['stats']['files'] = file_list
raw_output.append(output_line)
output_line = {}
message_lines = []
file_list = []
output_line = {
'commit': line_list[0],
'message': line_list[1]
}
continue
# all other styles
if line.startswith('commit '):
if output_line:
if message_lines:
output_line['message'] = '\n'.join(message_lines)
if file_list:
output_line['stats']['files'] = file_list
raw_output.append(output_line)
output_line = {}
message_lines = []
file_list = []
output_line['commit'] = line_list[1]
continue
if line.startswith('Merge: '):
output_line['merge'] = line_list[1]
continue
if line.startswith('Author: '):
values = line_list[1].rsplit(maxsplit=1)
output_line['author'] = values[0]
output_line['author_email'] = values[1].strip('<').strip('>')
continue
if line.startswith('Date: '):
output_line['date'] = line_list[1]
continue
if line.startswith('AuthorDate: '):
output_line['date'] = line_list[1]
continue
if line.startswith('CommitDate: '):
output_line['commit_by_date'] = line_list[1]
continue
if line.startswith('Commit: '):
values = line_list[1].rsplit(maxsplit=1)
output_line['commit_by'] = values[0]
output_line['commit_by_email'] = values[1].strip('<').strip('>')
continue
if line.startswith(' '):
message_lines.append(line.strip())
continue
if line.startswith(' ') and 'changed, ' not in line:
# this is a file name
file_name = line.split('|')[0].strip()
file_list.append(file_name)
continue
if line.startswith(' ') and 'changed, ' in line:
# this is the stat summary
changes = changes_pattern.match(line)
if changes:
files = changes['files']
insertions = changes['insertions']
deletions = changes['deletions']
output_line['stats'] = {
'files_changed': files or '0',
'insertions': insertions or '0',
'deletions': deletions or '0'
}
if output_line:
if message_lines:
output_line['message'] = '\n'.join(message_lines)
if file_list:
output_line['stats']['files'] = file_list
raw_output.append(output_line)
return raw_output if raw else _process(raw_output)

View File

@@ -63,7 +63,7 @@ import jc.utils
class info():
"""Provides parser metadata (version, author, etc.)"""
version = '1.6'
version = '1.7'
description = '`history` command parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
@@ -117,17 +117,14 @@ def parse(data, raw=False, quiet=False):
raw_output = {}
if jc.utils.has_data(data):
linedata = data.splitlines()
# split lines and clear out any non-ascii chars
linedata = data.encode('ascii', errors='ignore').decode().splitlines()
# Skip any blank lines
for entry in filter(None, linedata):
try:
parsed_line = entry.split(maxsplit=1)
raw_output[parsed_line[0]] = parsed_line[1]
except IndexError:
# need to catch indexerror in case there is weird input from prior commands
number, command = entry.split(maxsplit=1)
raw_output[number] = command
except ValueError:
# need to catch ValueError in case there is weird input from prior commands
pass
if raw:

View File

@@ -1,12 +1,14 @@
"""jc - JSON Convert `INI` file parser
Parses standard `INI` files and files containing simple key/value pairs.
Delimiter can be `=` or `:`. Missing values are supported. Comment prefix
can be `#` or `;`. Comments must be on their own line.
Note: Values starting and ending with quotation marks will have the marks
removed. If you would like to keep the quotation marks, use the `-r`
command-line argument or the `raw=True` argument in `parse()`.
- Delimiter can be `=` or `:`. Missing values are supported.
- Comment prefix can be `#` or `;`. Comments must be on their own line.
- If duplicate keys are found, only the last value will be used.
> Note: Values starting and ending with quotation marks will have the marks
removed. If you would like to keep the quotation marks, use the `-r`
command-line argument or the `raw=True` argument in `parse()`.
Usage (cli):
@@ -67,7 +69,7 @@ import configparser
class info():
"""Provides parser metadata (version, author, etc.)"""
version = '1.5'
version = '1.6'
description = 'INI file parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
@@ -135,7 +137,9 @@ def parse(data, raw=False, quiet=False):
if jc.utils.has_data(data):
ini = configparser.ConfigParser(allow_no_value=True, interpolation=None)
ini = configparser.ConfigParser(allow_no_value=True,
interpolation=None,
strict=False)
try:
ini.read_string(data)
raw_output = {s: dict(ini.items(s)) for s in ini.sections()}

View File

@@ -1,12 +1,14 @@
"""jc - JSON Convert `Key/Value` file parser
Supports files containing simple key/value pairs. Delimiter can be `=` or
`:`. Missing values are supported. Comment prefix can be `#` or `;`.
Comments must be on their own line.
Supports files containing simple key/value pairs.
Note: Values starting and ending with quotation marks will have the marks
removed. If you would like to keep the quotation marks, use the `-r`
command-line argument or the `raw=True` argument in `parse()`.
- Delimiter can be `=` or `:`. Missing values are supported.
- Comment prefix can be `#` or `;`. Comments must be on their own line.
- If duplicate keys are found, only the last value will be used.
> Note: Values starting and ending with quotation marks will have the marks
removed. If you would like to keep the quotation marks, use the `-r`
command-line argument or the `raw=True` argument in `parse()`.
Usage (cli):
@@ -52,7 +54,7 @@ Examples:
class info():
"""Provides parser metadata (version, author, etc.)"""
version = '1.1'
version = '1.2'
description = 'Key/Value file parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'

111
jc/parsers/update_alt_gs.py Normal file
View File

@@ -0,0 +1,111 @@
"""jc - JSON Convert `update-alternatives --get-selections` command output parser
Usage (cli):
$ update-alternatives --get-selections | jc --update-alt-gs
or
$ jc update-alternatives --get-selections
Usage (module):
import jc
result = jc.parse('update-alt-gs',
update_alternatives_get_selections_command_output)
Schema:
[
{
"name": string,
"status": string,
"current": string
}
]
Examples:
$ update-alternatives --get-selections | jc --update-alt-gs -p
[
{
"name": "arptables",
"status": "auto",
"current": "/usr/sbin/arptables-nft"
},
{
"name": "awk",
"status": "auto",
"current": "/usr/bin/gawk"
}
]
"""
from typing import List, Dict
import jc.utils
class info():
"""Provides parser metadata (version, author, etc.)"""
version = '1.0'
description = '`update-alternatives --get-selections` command parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
compatible = ['linux']
magic_commands = ['update-alternatives --get-selections']
__version__ = info.version
def _process(proc_data: List[Dict]) -> List[Dict]:
"""
Final processing to conform to the schema.
Parameters:
proc_data: (List of Dictionaries) raw structured data to process
Returns:
List of Dictionaries. Structured to conform to the schema.
"""
# nothing to process
return proc_data
def parse(
data: str,
raw: bool = False,
quiet: bool = False
) -> List[Dict]:
"""
Main text parsing function
Parameters:
data: (string) text data to parse
raw: (boolean) unprocessed output if True
quiet: (boolean) suppress warning messages if True
Returns:
List of Dictionaries. Raw or processed structured data.
"""
jc.utils.compatibility(__name__, info.compatible, quiet)
jc.utils.input_type_check(data)
raw_output: List = []
output_line = {}
if jc.utils.has_data(data):
for line in filter(None, data.splitlines()):
line_list = line.split(maxsplit=2)
output_line = {
"name": line_list[0],
"status": line_list[1],
"current": line_list[2]
}
raw_output.append(output_line)
return raw_output if raw else _process(raw_output)

263
jc/parsers/update_alt_q.py Normal file
View File

@@ -0,0 +1,263 @@
"""jc - JSON Convert `update-alternatives --query` command output parser
Usage (cli):
$ update-alternatives --query | jc --update-alt-q
or
$ jc update-alternatives --query
Usage (module):
import jc
result = jc.parse('update_alt_q',
update_alternatives_query_command_output)
Schema:
{
"name": string,
"link": string,
"slaves": [
{
"name": string,
"path": string
}
],
"status": string,
"best": string,
"value": string, # (null if 'none')
"alternatives": [
{
"alternative": string,
"priority": integer,
"slaves": [
{
"name": string,
"path": string
}
]
}
]
}
Examples:
$ update-alternatives --query editor | jc --update-alt-q -p
{
"name": "editor",
"link": "/usr/bin/editor",
"slaves": [
{
"name": "editor.1.gz",
"path": "/usr/share/man/man1/editor.1.gz"
},
{
"name": "editor.da.1.gz",
"path": "/usr/share/man/da/man1/editor.1.gz"
}
],
"status": "auto",
"best": "/bin/nano",
"value": "/bin/nano",
"alternatives": [
{
"alternative": "/bin/ed",
"priority": -100,
"slaves": [
{
"name": "editor.1.gz",
"path": "/usr/share/man/man1/ed.1.gz"
}
]
},
{
"alternative": "/bin/nano",
"priority": 40,
"slaves": [
{
"name": "editor.1.gz",
"path": "/usr/share/man/man1/nano.1.gz"
}
]
}
]
}
$ update-alternatives --query | jc --update-alt-q -p -r
{
"name": "editor",
"link": "/usr/bin/editor",
"slaves": [
{
"name": "editor.1.gz",
"path": "/usr/share/man/man1/editor.1.gz"
},
{
"name": "editor.da.1.gz",
"path": "/usr/share/man/da/man1/editor.1.gz"
}
],
"status": "auto",
"best": "/bin/nano",
"value": "/bin/nano",
"alternatives": [
{
"alternative": "/bin/ed",
"priority": "-100",
"slaves": [
{
"name": "editor.1.gz",
"path": "/usr/share/man/man1/ed.1.gz"
}
]
},
{
"alternative": "/bin/nano",
"priority": "40",
"slaves": [
{
"name": "editor.1.gz",
"path": "/usr/share/man/man1/nano.1.gz"
}
]
}
]
}
"""
from typing import List, Dict
import jc.utils
class info():
"""Provides parser metadata (version, author, etc.)"""
version = '1.1'
description = '`update-alternatives --query` command parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
compatible = ['linux']
magic_commands = ['update-alternatives --query']
__version__ = info.version
def _process(proc_data: Dict) -> Dict:
"""
Final processing to conform to the schema.
Parameters:
proc_data: (Dictionary) raw structured data to process
Returns:
Dictionary. Structured to conform to the schema.
"""
int_list = ['priority']
if 'value' in proc_data:
if proc_data['value'] == 'none':
proc_data['value'] = None
if 'alternatives' in proc_data:
for index, alt in enumerate(proc_data['alternatives']):
for key in alt:
if key in int_list:
proc_data['alternatives'][index][key] = jc.utils.convert_to_int(proc_data['alternatives'][index][key])
return proc_data
def parse(
data: str,
raw: bool = False,
quiet: bool = False
) -> Dict:
"""
Main text parsing function
Parameters:
data: (string) text data to parse
raw: (boolean) unprocessed output if True
quiet: (boolean) suppress warning messages if True
Returns:
Dictionary. Raw or processed structured data.
"""
jc.utils.compatibility(__name__, info.compatible, quiet)
jc.utils.input_type_check(data)
raw_output: Dict = {}
slaves: List = []
alt_obj: Dict = {}
if jc.utils.has_data(data):
for line in filter(None, data.splitlines()):
line_list = line.split(maxsplit=1)
if line.startswith('Name: '):
raw_output['name'] = line_list[1]
continue
if line.startswith('Link: '):
raw_output['link'] = line_list[1]
continue
if line.startswith('Slaves:'):
continue
if line.startswith(' '):
s_name = line_list[0].strip()
s_path = line_list[1]
slaves.append(
{
"name": s_name,
"path": s_path
}
)
continue
if line.startswith('Status: '):
if slaves:
raw_output['slaves'] = slaves
slaves = []
raw_output['status'] = line_list[1]
continue
if line.startswith('Best: '):
raw_output['best'] = line_list[1]
continue
if line.startswith('Value: '):
raw_output['value'] = line_list[1]
continue
if line.startswith('Alternative: '):
if not 'alternatives' in raw_output:
raw_output['alternatives'] = []
if alt_obj:
if slaves:
alt_obj['slaves'] = slaves
slaves = []
raw_output['alternatives'].append(alt_obj)
alt_obj = {"alternative": line_list[1]}
continue
if line.startswith('Priority: '):
alt_obj['priority'] = line_list[1]
continue
if alt_obj:
if slaves:
alt_obj['slaves'] = slaves
raw_output['alternatives'].append(alt_obj)
return raw_output if raw else _process(raw_output)

View File

@@ -49,7 +49,8 @@ Schema:
"offset_width": integer,
"offset_height": integer,
"dimension_width": integer,
"dimension_height": integer
"dimension_height": integer,
"rotation": string
}
],
"unassociated_devices": [
@@ -125,7 +126,8 @@ Examples:
"offset_width": 0,
"offset_height": 0,
"dimension_width": 310,
"dimension_height": 170
"dimension_height": 170,
"rotation": "normal"
}
}
],
@@ -140,7 +142,7 @@ import jc.utils
class info:
"""Provides parser metadata (version, author, etc.)"""
version = "1.0"
version = "1.1"
description = "`xrandr` command parser"
author = "Kevin Lyter"
author_email = "lyter_git at sent.com"
@@ -252,6 +254,7 @@ _device_pattern = (
+ "(?P<is_primary> primary)? ?"
+ "((?P<resolution_width>\d+)x(?P<resolution_height>\d+)"
+ "\+(?P<offset_width>\d+)\+(?P<offset_height>\d+))? "
+ "(?P<rotation>(inverted|left|right))? ?"
+ "\(normal left inverted right x axis y axis\)"
+ "( ((?P<dimension_width>\d+)mm x (?P<dimension_height>\d+)mm)?)?"
)
@@ -275,9 +278,10 @@ def _parse_device(next_lines: List[str], quiet: bool = False) -> Optional[Device
"is_primary": matches["is_primary"] is not None
and len(matches["is_primary"]) > 0,
"device_name": matches["device_name"],
"rotation": matches["rotation"] or "normal",
}
for k, v in matches.items():
if k not in {"is_connected", "is_primary", "device_name"}:
if k not in {"is_connected", "is_primary", "device_name", "rotation"}:
try:
if v:
device[k] = int(v)

View File

@@ -1,5 +1,7 @@
"""jc - JSON Convert `YAML` file parser
Note: datetime objects will be converted to strings.
Usage (cli):
$ cat foo.yaml | jc --yaml
@@ -85,7 +87,7 @@ from jc.exceptions import LibraryNotInstalled
class info():
"""Provides parser metadata (version, author, etc.)"""
version = '1.6'
version = '1.7'
description = 'YAML file parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
@@ -147,6 +149,11 @@ def parse(data, raw=False, quiet=False):
yaml = YAML(typ='safe')
# modify the timestamp constructor to output datetime objects as
# strings since JSON does not support datetime objects
yaml.constructor.yaml_constructors['tag:yaml.org,2002:timestamp'] = \
yaml.constructor.yaml_constructors['tag:yaml.org,2002:str']
for document in yaml.load_all(data):
raw_output.append(document)

View File

@@ -6,7 +6,27 @@ import shutil
from datetime import datetime, timezone
from textwrap import TextWrapper
from functools import lru_cache
from typing import List, Tuple, Union, Optional
from typing import List, Iterable, Union, Optional
def _asciify(string: str) -> str:
"""
Return a string downgraded from Unicode to ASCII with some simple
conversions.
"""
string = string.replace('©', '(c)')
# the ascii() function adds single quotes around the string
string = ascii(string)[1:-1]
string = string.replace(r'\n', '\n')
return string
def _safe_print(string: str, sep=' ', end='\n', file=sys.stdout, flush=False) -> None:
"""Output for both UTF-8 and ASCII encoding systems"""
try:
print(string, sep=sep, end=end, file=file, flush=flush)
except UnicodeEncodeError:
print(_asciify(string), sep=sep, end=end, file=file, flush=flush)
def warning_message(message_lines: List[str]) -> None:
@@ -36,13 +56,13 @@ def warning_message(message_lines: List[str]) -> None:
first_line = message_lines.pop(0)
first_str = f'jc: Warning - {first_line}'
first_str = first_wrapper.fill(first_str)
print(first_str, file=sys.stderr)
_safe_print(first_str, file=sys.stderr)
for line in message_lines:
if line == '':
continue
message = next_wrapper.fill(line)
print(message, file=sys.stderr)
_safe_print(message, file=sys.stderr)
def error_message(message_lines: List[str]) -> None:
@@ -68,13 +88,13 @@ def error_message(message_lines: List[str]) -> None:
first_line = message_lines.pop(0)
first_str = f'jc: Error - {first_line}'
first_str = first_wrapper.fill(first_str)
print(first_str, file=sys.stderr)
_safe_print(first_str, file=sys.stderr)
for line in message_lines:
if line == '':
continue
message = next_wrapper.fill(line)
print(message, file=sys.stderr)
_safe_print(message, file=sys.stderr)
def compatibility(mod_name: str, compatible: List, quiet: bool = False) -> None:
@@ -233,7 +253,7 @@ def input_type_check(data: str) -> None:
class timestamp:
def __init__(self,
datetime_string: str,
format_hint: Union[List, Tuple, None] = None
format_hint: Optional[Iterable] = None
) -> None:
"""
Input a datetime text string of several formats and convert to a
@@ -244,7 +264,7 @@ class timestamp:
datetime_string (str): a string representation of a
datetime in several supported formats
format_hint (list | tuple): an optional list of format ID
format_hint (iterable): an optional iterable of format ID
integers to instruct the timestamp object to try those
formats first in the order given. Other formats will be
tried after the format hint list is exhausted. This can
@@ -361,6 +381,7 @@ class timestamp:
formats = [
{'id': 1000, 'format': '%a %b %d %H:%M:%S %Y', 'locale': None}, # manual C locale format conversion: Tue Mar 23 16:12:11 2021 or Tue Mar 23 16:12:11 IST 2021
{'id': 1100, 'format': '%a %b %d %H:%M:%S %Y %z', 'locale': None}, # git date output: Thu Mar 5 09:17:40 2020 -0800
{'id': 1500, 'format': '%Y-%m-%d %H:%M', 'locale': None}, # en_US.UTF-8 local format (found in who cli output): 2021-03-23 00:14
{'id': 1600, 'format': '%m/%d/%Y %I:%M %p', 'locale': None}, # Windows english format (found in dir cli output): 12/07/2019 02:09 AM
{'id': 1700, 'format': '%m/%d/%Y, %I:%M:%S %p', 'locale': None}, # Windows english format wint non-UTC tz (found in systeminfo cli output): 3/22/2021, 1:15:51 PM (UTC-0600)

View File

@@ -1,4 +1,4 @@
.TH jc 1 2022-03-25 1.18.6 "JSON Convert"
.TH jc 1 2022-04-27 1.18.8 "JSON Convert"
.SH NAME
jc \- JSONifies the output of many CLI tools and file-types
.SH SYNOPSIS
@@ -137,6 +137,11 @@ CSV file streaming parser
\fB--fstab\fP
`/etc/fstab` file parser
.TP
.B
\fB--git-log\fP
`git log` command parser
.TP
.B
\fB--group\fP
@@ -437,6 +442,16 @@ Key/Value file parser
\fB--uname\fP
`uname -a` command parser
.TP
.B
\fB--update-alt-gs\fP
`update-alternatives --get-selections` command parser
.TP
.B
\fB--update-alt-q\fP
`update-alternatives --query` command parser
.TP
.B
\fB--upower\fP
@@ -671,6 +686,8 @@ or by exporting to the environment before running commands:
$ export LANG=C
.RE
On some older systems UTF-8 output will be downgraded to ASCII with `\\u` escape sequences if the \fBC\fP locale does not support UTF-8 encoding.
\fBTimezones:\fP Some parsers have calculated epoch timestamp fields added to the output. Unless a timestamp field name has a \fB_utc\fP suffix it is considered naive. (i.e. based on the local timezone of the system the \fBjc\fP parser was run on).
If a UTC timezone can be detected in the text of the command output, the timestamp will be timezone aware and have a \fB_utc\fP suffix on the key name. (e.g. \fBepoch_utc\fP) No other timezones are supported for aware timestamps.

View File

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

View File

@@ -201,6 +201,8 @@ or by exporting to the environment before running commands:
$ export LANG=C
.RE
On some older systems UTF-8 output will be downgraded to ASCII with `\\u` escape sequences if the \fBC\fP locale does not support UTF-8 encoding.
\fBTimezones:\fP Some parsers have calculated epoch timestamp fields added to the output. Unless a timestamp field name has a \fB_utc\fP suffix it is considered naive. (i.e. based on the local timezone of the system the \fBjc\fP parser was run on).
If a UTC timezone can be detected in the text of the command output, the timestamp will be timezone aware and have a \fB_utc\fP suffix on the key name. (e.g. \fBepoch_utc\fP) No other timezones are supported for aware timestamps.

View File

@@ -106,17 +106,18 @@ pip3 install jc
### OS Package Repositories
| OS | Command |
|-----------------------|-------------------------------------------------------------------------------|
| Debian/Ubuntu linux | `apt-get install jc` |
| Fedora linux | `dnf install jc` |
| openSUSE linux | `zypper install jc` |
| Arch linux | `pacman -S jc` |
| NixOS linux | `nix-env -iA nixpkgs.jc` or `nix-env -iA nixos.jc` |
| Guix System linux | `guix install jc` |
| macOS | `brew install jc` |
| FreeBSD | `portsnap fetch update && cd /usr/ports/textproc/py-jc && make install clean` |
| Ansible filter plugin | `ansible-galaxy collection install community.general` |
| OS | Command |
|--------------------------------------|-------------------------------------------------------------------------------|
| Debian/Ubuntu linux | `apt-get install jc` |
| Fedora linux | `dnf install jc` |
| openSUSE linux | `zypper install jc` |
| Archlinux User Repositories (AUR) | `paru -S jc` or `aura -A jc` or `yay -S jc` |
| NixOS linux | `nix-env -iA nixpkgs.jc` or `nix-env -iA nixos.jc` |
| Guix System linux | `guix install jc` |
| macOS | `brew install jc` |
| FreeBSD | `portsnap fetch update && cd /usr/ports/textproc/py-jc && make install clean` |
| Ansible filter plugin | `ansible-galaxy collection install community.general` |
| Gentoo Linux | `emerge dev-python/jc` |
> For more OS Packages, see https://repology.org/project/jc/versions.
@@ -305,9 +306,9 @@ Local parser plugins are standard python module files. Use the
or [`jc/parsers/foo_s.py (streaming)`](https://github.com/kellyjonbrazil/jc/blob/master/jc/parsers/foo_s.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 parsers.
Local plugin filenames must be valid python module names and therefore must
start with a letter and consist entirely of alphanumerics. Local plugins
may override default parsers.
> Note: The application data directory follows the
[XDG Base Directory Specification](https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html)
@@ -327,6 +328,9 @@ or by exporting to the environment before running commands:
$ export LANG=C
```
On some older systems UTF-8 output will be downgraded to ASCII with `\\u`
escape sequences if the `C` locale does not support UTF-8 encoding.
#### Timezones
Some parsers have calculated epoch timestamp fields added to the output. Unless

File diff suppressed because one or more lines are too long

View File

@@ -998,3 +998,4 @@
1062 ls
1063 cd testfiles/
1064 history > history.out
1065 export MYTEST=©2019-2022

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

14966
tests/fixtures/generic/git-log-full.out vendored Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

19832
tests/fixtures/generic/git-log-fuller.out vendored Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

14966
tests/fixtures/generic/git-log-medium.out vendored Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

2433
tests/fixtures/generic/git-log-oneline.out vendored Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

12280
tests/fixtures/generic/git-log-short.out vendored Normal file

File diff suppressed because it is too large Load Diff

1
tests/fixtures/generic/git-log.json vendored Normal file

File diff suppressed because one or more lines are too long

14966
tests/fixtures/generic/git-log.out vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1 @@
[{"name":"arptables","status":"auto","current":"/usr/sbin/arptables-nft"},{"name":"awk","status":"auto","current":"/usr/bin/gawk"},{"name":"builtins.7.gz","status":"auto","current":"/usr/share/man/man7/bash-builtins.7.gz"},{"name":"c++","status":"auto","current":"/usr/bin/g++"},{"name":"c89","status":"auto","current":"/usr/bin/c89-gcc"},{"name":"c99","status":"auto","current":"/usr/bin/c99-gcc"},{"name":"cc","status":"auto","current":"/usr/bin/gcc"},{"name":"cpp","status":"auto","current":"/usr/bin/cpp"},{"name":"ebtables","status":"auto","current":"/usr/sbin/ebtables-nft"},{"name":"editor","status":"auto","current":"/bin/nano"},{"name":"ex","status":"auto","current":"/usr/bin/vim.basic"},{"name":"fakeroot","status":"auto","current":"/usr/bin/fakeroot-sysv"},{"name":"ftp","status":"auto","current":"/usr/bin/netkit-ftp"},{"name":"infobrowser","status":"auto","current":"/usr/bin/info"},{"name":"ip6tables","status":"auto","current":"/usr/sbin/ip6tables-nft"},{"name":"iptables","status":"auto","current":"/usr/sbin/iptables-nft"},{"name":"jsondiff","status":"auto","current":"/usr/bin/json-patch-jsondiff"},{"name":"lzma","status":"auto","current":"/usr/bin/xz"},{"name":"mt","status":"auto","current":"/bin/mt-gnu"},{"name":"nc","status":"auto","current":"/bin/nc.openbsd"},{"name":"newt-palette","status":"auto","current":"/etc/newt/palette.ubuntu"},{"name":"pager","status":"auto","current":"/usr/bin/less"},{"name":"pico","status":"auto","current":"/bin/nano"},{"name":"pinentry","status":"auto","current":"/usr/bin/pinentry-curses"},{"name":"rcp","status":"auto","current":"/usr/bin/scp"},{"name":"rlogin","status":"auto","current":"/usr/bin/slogin"},{"name":"rmt","status":"auto","current":"/usr/sbin/rmt-tar"},{"name":"rsh","status":"auto","current":"/usr/bin/ssh"},{"name":"rview","status":"auto","current":"/usr/bin/vim.basic"},{"name":"rvim","status":"auto","current":"/usr/bin/vim.basic"},{"name":"sar","status":"auto","current":"/usr/bin/sar.sysstat"},{"name":"telnet","status":"auto","current":"/usr/bin/telnet.netkit"},{"name":"text.plymouth","status":"auto","current":"/usr/share/plymouth/themes/ubuntu-text/ubuntu-text.plymouth"},{"name":"traceroute6","status":"auto","current":"/usr/bin/traceroute6.iputils"},{"name":"vi","status":"auto","current":"/usr/bin/vim.basic"},{"name":"view","status":"auto","current":"/usr/bin/vim.basic"},{"name":"vim","status":"auto","current":"/usr/bin/vim.basic"},{"name":"vimdiff","status":"auto","current":"/usr/bin/vim.basic"},{"name":"vtrgb","status":"auto","current":"/etc/console-setup/vtrgb"},{"name":"w","status":"auto","current":"/usr/bin/w.procps"},{"name":"write","status":"auto","current":"/usr/bin/write.ul"}]

View File

@@ -0,0 +1,41 @@
arptables auto /usr/sbin/arptables-nft
awk auto /usr/bin/gawk
builtins.7.gz auto /usr/share/man/man7/bash-builtins.7.gz
c++ auto /usr/bin/g++
c89 auto /usr/bin/c89-gcc
c99 auto /usr/bin/c99-gcc
cc auto /usr/bin/gcc
cpp auto /usr/bin/cpp
ebtables auto /usr/sbin/ebtables-nft
editor auto /bin/nano
ex auto /usr/bin/vim.basic
fakeroot auto /usr/bin/fakeroot-sysv
ftp auto /usr/bin/netkit-ftp
infobrowser auto /usr/bin/info
ip6tables auto /usr/sbin/ip6tables-nft
iptables auto /usr/sbin/iptables-nft
jsondiff auto /usr/bin/json-patch-jsondiff
lzma auto /usr/bin/xz
mt auto /bin/mt-gnu
nc auto /bin/nc.openbsd
newt-palette auto /etc/newt/palette.ubuntu
pager auto /usr/bin/less
pico auto /bin/nano
pinentry auto /usr/bin/pinentry-curses
rcp auto /usr/bin/scp
rlogin auto /usr/bin/slogin
rmt auto /usr/sbin/rmt-tar
rsh auto /usr/bin/ssh
rview auto /usr/bin/vim.basic
rvim auto /usr/bin/vim.basic
sar auto /usr/bin/sar.sysstat
telnet auto /usr/bin/telnet.netkit
text.plymouth auto /usr/share/plymouth/themes/ubuntu-text/ubuntu-text.plymouth
traceroute6 auto /usr/bin/traceroute6.iputils
vi auto /usr/bin/vim.basic
view auto /usr/bin/vim.basic
vim auto /usr/bin/vim.basic
vimdiff auto /usr/bin/vim.basic
vtrgb auto /etc/console-setup/vtrgb
w auto /usr/bin/w.procps
write auto /usr/bin/write.ul

View File

@@ -0,0 +1 @@
{"name":"editor","link":"/usr/bin/editor","slaves":[{"name":"editor.1.gz","path":"/usr/share/man/man1/editor.1.gz"},{"name":"editor.da.1.gz","path":"/usr/share/man/da/man1/editor.1.gz"},{"name":"editor.de.1.gz","path":"/usr/share/man/de/man1/editor.1.gz"},{"name":"editor.fr.1.gz","path":"/usr/share/man/fr/man1/editor.1.gz"},{"name":"editor.it.1.gz","path":"/usr/share/man/it/man1/editor.1.gz"},{"name":"editor.ja.1.gz","path":"/usr/share/man/ja/man1/editor.1.gz"},{"name":"editor.pl.1.gz","path":"/usr/share/man/pl/man1/editor.1.gz"},{"name":"editor.ru.1.gz","path":"/usr/share/man/ru/man1/editor.1.gz"}],"status":"auto","best":"/bin/nano","value":"/bin/nano","alternatives":[{"alternative":"/bin/ed","priority":-100,"slaves":[{"name":"editor.1.gz","path":"/usr/share/man/man1/ed.1.gz"}]},{"alternative":"/bin/nano","priority":40,"slaves":[{"name":"editor.1.gz","path":"/usr/share/man/man1/nano.1.gz"}]},{"alternative":"/usr/bin/vim.basic","priority":30,"slaves":[{"name":"editor.1.gz","path":"/usr/share/man/man1/vim.1.gz"},{"name":"editor.da.1.gz","path":"/usr/share/man/da/man1/vim.1.gz"},{"name":"editor.de.1.gz","path":"/usr/share/man/de/man1/vim.1.gz"},{"name":"editor.fr.1.gz","path":"/usr/share/man/fr/man1/vim.1.gz"},{"name":"editor.it.1.gz","path":"/usr/share/man/it/man1/vim.1.gz"},{"name":"editor.ja.1.gz","path":"/usr/share/man/ja/man1/vim.1.gz"},{"name":"editor.pl.1.gz","path":"/usr/share/man/pl/man1/vim.1.gz"},{"name":"editor.ru.1.gz","path":"/usr/share/man/ru/man1/vim.1.gz"}]},{"alternative":"/usr/bin/vim.tiny","priority":15,"slaves":[{"name":"editor.1.gz","path":"/usr/share/man/man1/vim.1.gz"},{"name":"editor.da.1.gz","path":"/usr/share/man/da/man1/vim.1.gz"},{"name":"editor.de.1.gz","path":"/usr/share/man/de/man1/vim.1.gz"},{"name":"editor.fr.1.gz","path":"/usr/share/man/fr/man1/vim.1.gz"},{"name":"editor.it.1.gz","path":"/usr/share/man/it/man1/vim.1.gz"},{"name":"editor.ja.1.gz","path":"/usr/share/man/ja/man1/vim.1.gz"},{"name":"editor.pl.1.gz","path":"/usr/share/man/pl/man1/vim.1.gz"},{"name":"editor.ru.1.gz","path":"/usr/share/man/ru/man1/vim.1.gz"}]}]}

View File

@@ -0,0 +1,48 @@
Name: editor
Link: /usr/bin/editor
Slaves:
editor.1.gz /usr/share/man/man1/editor.1.gz
editor.da.1.gz /usr/share/man/da/man1/editor.1.gz
editor.de.1.gz /usr/share/man/de/man1/editor.1.gz
editor.fr.1.gz /usr/share/man/fr/man1/editor.1.gz
editor.it.1.gz /usr/share/man/it/man1/editor.1.gz
editor.ja.1.gz /usr/share/man/ja/man1/editor.1.gz
editor.pl.1.gz /usr/share/man/pl/man1/editor.1.gz
editor.ru.1.gz /usr/share/man/ru/man1/editor.1.gz
Status: auto
Best: /bin/nano
Value: /bin/nano
Alternative: /bin/ed
Priority: -100
Slaves:
editor.1.gz /usr/share/man/man1/ed.1.gz
Alternative: /bin/nano
Priority: 40
Slaves:
editor.1.gz /usr/share/man/man1/nano.1.gz
Alternative: /usr/bin/vim.basic
Priority: 30
Slaves:
editor.1.gz /usr/share/man/man1/vim.1.gz
editor.da.1.gz /usr/share/man/da/man1/vim.1.gz
editor.de.1.gz /usr/share/man/de/man1/vim.1.gz
editor.fr.1.gz /usr/share/man/fr/man1/vim.1.gz
editor.it.1.gz /usr/share/man/it/man1/vim.1.gz
editor.ja.1.gz /usr/share/man/ja/man1/vim.1.gz
editor.pl.1.gz /usr/share/man/pl/man1/vim.1.gz
editor.ru.1.gz /usr/share/man/ru/man1/vim.1.gz
Alternative: /usr/bin/vim.tiny
Priority: 15
Slaves:
editor.1.gz /usr/share/man/man1/vim.1.gz
editor.da.1.gz /usr/share/man/da/man1/vim.1.gz
editor.de.1.gz /usr/share/man/de/man1/vim.1.gz
editor.fr.1.gz /usr/share/man/fr/man1/vim.1.gz
editor.it.1.gz /usr/share/man/it/man1/vim.1.gz
editor.ja.1.gz /usr/share/man/ja/man1/vim.1.gz
editor.pl.1.gz /usr/share/man/pl/man1/vim.1.gz
editor.ru.1.gz /usr/share/man/ru/man1/vim.1.gz

View File

@@ -0,0 +1 @@
{"name":"php-fpm.sock","link":"/run/php/php-fpm.sock","status":"auto","best":"/run/php/php8.1-fpm.sock","value":"/run/php/php8.1-fpm.sock","alternatives":[{"alternative":"/run/php/php7.4-fpm.sock","priority":74},{"alternative":"/run/php/php8.0-fpm.sock","priority":80},{"alternative":"/run/php/php8.1-fpm.sock","priority":81}]}

View File

@@ -0,0 +1,14 @@
Name: php-fpm.sock
Link: /run/php/php-fpm.sock
Status: auto
Best: /run/php/php8.1-fpm.sock
Value: /run/php/php8.1-fpm.sock
Alternative: /run/php/php7.4-fpm.sock
Priority: 74
Alternative: /run/php/php8.0-fpm.sock
Priority: 80
Alternative: /run/php/php8.1-fpm.sock
Priority: 81

View File

@@ -43,6 +43,7 @@
"is_connected": true,
"is_primary": true,
"device_name": "eDP1",
"rotation": "normal",
"resolution_width": 1920,
"resolution_height": 1080,
"offset_width": 0,

215
tests/test_git_log.py Normal file
View File

@@ -0,0 +1,215 @@
import os
import unittest
import json
import jc.parsers.git_log
THIS_DIR = os.path.dirname(os.path.abspath(__file__))
class MyTests(unittest.TestCase):
def setUp(self):
# input
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/generic/git-log.out'), 'r', encoding='utf-8') as f:
self.git_log = f.read()
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/generic/git-log-short.out'), 'r', encoding='utf-8') as f:
self.git_log_short = f.read()
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/generic/git-log-short-stat.out'), 'r', encoding='utf-8') as f:
self.git_log_short_stat = f.read()
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/generic/git-log-short-shortstat.out'), 'r', encoding='utf-8') as f:
self.git_log_short_shortstat = f.read()
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/generic/git-log-medium.out'), 'r', encoding='utf-8') as f:
self.git_log_medium = f.read()
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/generic/git-log-medium-stat.out'), 'r', encoding='utf-8') as f:
self.git_log_medium_stat = f.read()
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/generic/git-log-medium-shortstat.out'), 'r', encoding='utf-8') as f:
self.git_log_medium_shortstat = f.read()
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/generic/git-log-full.out'), 'r', encoding='utf-8') as f:
self.git_log_full = f.read()
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/generic/git-log-full-stat.out'), 'r', encoding='utf-8') as f:
self.git_log_full_stat = f.read()
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/generic/git-log-full-shortstat.out'), 'r', encoding='utf-8') as f:
self.git_log_full_shortstat = f.read()
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/generic/git-log-fuller.out'), 'r', encoding='utf-8') as f:
self.git_log_fuller = f.read()
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/generic/git-log-fuller-stat.out'), 'r', encoding='utf-8') as f:
self.git_log_fuller_stat = f.read()
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/generic/git-log-fuller-shortstat.out'), 'r', encoding='utf-8') as f:
self.git_log_fuller_shortstat = f.read()
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/generic/git-log-oneline.out'), 'r', encoding='utf-8') as f:
self.git_log_oneline = f.read()
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/generic/git-log-oneline-stat.out'), 'r', encoding='utf-8') as f:
self.git_log_oneline_stat = f.read()
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/generic/git-log-oneline-shortstat.out'), 'r', encoding='utf-8') as f:
self.git_log_oneline_shortstat = f.read()
# output
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/generic/git-log.json'), 'r', encoding='utf-8') as f:
self.git_log_json = json.loads(f.read())
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/generic/git-log-short.json'), 'r', encoding='utf-8') as f:
self.git_log_short_json = json.loads(f.read())
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/generic/git-log-short-stat.json'), 'r', encoding='utf-8') as f:
self.git_log_short_stat_json = json.loads(f.read())
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/generic/git-log-short-shortstat.json'), 'r', encoding='utf-8') as f:
self.git_log_short_shortstat_json = json.loads(f.read())
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/generic/git-log-medium.json'), 'r', encoding='utf-8') as f:
self.git_log_medium_json = json.loads(f.read())
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/generic/git-log-medium-stat.json'), 'r', encoding='utf-8') as f:
self.git_log_medium_stat_json = json.loads(f.read())
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/generic/git-log-medium-shortstat.json'), 'r', encoding='utf-8') as f:
self.git_log_medium_shortstat_json = json.loads(f.read())
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/generic/git-log-full.json'), 'r', encoding='utf-8') as f:
self.git_log_full_json = json.loads(f.read())
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/generic/git-log-full-stat.json'), 'r', encoding='utf-8') as f:
self.git_log_full_stat_json = json.loads(f.read())
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/generic/git-log-full-shortstat.json'), 'r', encoding='utf-8') as f:
self.git_log_full_shortstat_json = json.loads(f.read())
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/generic/git-log-fuller.json'), 'r', encoding='utf-8') as f:
self.git_log_fuller_json = json.loads(f.read())
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/generic/git-log-fuller-stat.json'), 'r', encoding='utf-8') as f:
self.git_log_fuller_stat_json = json.loads(f.read())
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/generic/git-log-fuller-shortstat.json'), 'r', encoding='utf-8') as f:
self.git_log_fuller_shortstat_json = json.loads(f.read())
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/generic/git-log-oneline.json'), 'r', encoding='utf-8') as f:
self.git_log_oneline_json = json.loads(f.read())
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/generic/git-log-oneline-stat.json'), 'r', encoding='utf-8') as f:
self.git_log_oneline_stat_json = json.loads(f.read())
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/generic/git-log-oneline-shortstat.json'), 'r', encoding='utf-8') as f:
self.git_log_oneline_shortstat_json = json.loads(f.read())
def test_git_log_nodata(self):
"""
Test 'git_log' with no data
"""
self.assertEqual(jc.parsers.git_log.parse('', quiet=True), [])
def test_git_log(self):
"""
Test 'git_log'
"""
self.assertEqual(jc.parsers.git_log.parse(self.git_log, quiet=True), self.git_log_json)
def test_git_log_short(self):
"""
Test 'git_log --format=short'
"""
self.assertEqual(jc.parsers.git_log.parse(self.git_log_short, quiet=True), self.git_log_short_json)
def test_git_log_short_stat(self):
"""
Test 'git_log --format=short --stat'
"""
self.assertEqual(jc.parsers.git_log.parse(self.git_log_short_stat, quiet=True), self.git_log_short_stat_json)
def test_git_log_short_shortstat(self):
"""
Test 'git_log --format=short --shortstat'
"""
self.assertEqual(jc.parsers.git_log.parse(self.git_log_short_shortstat, quiet=True), self.git_log_short_shortstat_json)
def test_git_log_medium(self):
"""
Test 'git_log --format=medium'
"""
self.assertEqual(jc.parsers.git_log.parse(self.git_log_medium, quiet=True), self.git_log_medium_json)
def test_git_log_medium_stat(self):
"""
Test 'git_log --format=medium --stat'
"""
self.assertEqual(jc.parsers.git_log.parse(self.git_log_medium_stat, quiet=True), self.git_log_medium_stat_json)
def test_git_log_medium_shortstat(self):
"""
Test 'git_log --format=medium --shortstat'
"""
self.assertEqual(jc.parsers.git_log.parse(self.git_log_medium_shortstat, quiet=True), self.git_log_medium_shortstat_json)
def test_git_log_full(self):
"""
Test 'git_log --format=full'
"""
self.assertEqual(jc.parsers.git_log.parse(self.git_log_full, quiet=True), self.git_log_full_json)
def test_git_log_full_stat(self):
"""
Test 'git_log --format=full --stat'
"""
self.assertEqual(jc.parsers.git_log.parse(self.git_log_full_stat, quiet=True), self.git_log_full_stat_json)
def test_git_log_full_shortstat(self):
"""
Test 'git_log --format=full --shortstat'
"""
self.assertEqual(jc.parsers.git_log.parse(self.git_log_full_shortstat, quiet=True), self.git_log_full_shortstat_json)
def test_git_log_fuller(self):
"""
Test 'git_log --format=fuller'
"""
self.assertEqual(jc.parsers.git_log.parse(self.git_log_fuller, quiet=True), self.git_log_fuller_json)
def test_git_log_fuller_stat(self):
"""
Test 'git_log --format=fuller --stat'
"""
self.assertEqual(jc.parsers.git_log.parse(self.git_log_fuller_stat, quiet=True), self.git_log_fuller_stat_json)
def test_git_log_fuller_shortstat(self):
"""
Test 'git_log --format=fuller --shortstat'
"""
self.assertEqual(jc.parsers.git_log.parse(self.git_log_fuller_shortstat, quiet=True), self.git_log_fuller_shortstat_json)
def test_git_log_oneline(self):
"""
Test 'git_log --format=oneline'
"""
self.assertEqual(jc.parsers.git_log.parse(self.git_log_oneline, quiet=True), self.git_log_oneline_json)
def test_git_log_oneline_stat(self):
"""
Test 'git_log --format=oneline --stat'
"""
self.assertEqual(jc.parsers.git_log.parse(self.git_log_oneline_stat, quiet=True), self.git_log_oneline_stat_json)
def test_git_log_oneline_shortstat(self):
"""
Test 'git_log --format=oneline --shortstat'
"""
self.assertEqual(jc.parsers.git_log.parse(self.git_log_oneline_shortstat, quiet=True), self.git_log_oneline_shortstat_json)
if __name__ == '__main__':
unittest.main()

View File

@@ -41,6 +41,18 @@ class MyTests(unittest.TestCase):
"""
self.assertEqual(jc.parsers.ini.parse(self.generic_ini_iptelserver, quiet=True), self.generic_ini_iptelserver_json)
def test_ini_duplicate_keys(self):
"""
Test input that contains duplicate keys. Only the last value should be used.
"""
data = '''
duplicate_key: value1
another_key = foo
duplicate_key = value2
'''
expected = {'duplicate_key': 'value2', 'another_key': 'foo'}
self.assertEqual(jc.parsers.ini.parse(data, quiet=True), expected)
if __name__ == '__main__':
unittest.main()

View File

@@ -41,6 +41,18 @@ class MyTests(unittest.TestCase):
"""
self.assertEqual(jc.parsers.kv.parse(self.generic_ini_keyvalue_ifcfg, quiet=True), self.generic_ini_keyvalue_ifcfg_json)
def test_kv_duplicate_keys(self):
"""
Test input that contains duplicate keys. Only the last value should be used.
"""
data = '''
duplicate_key: value1
another_key = foo
duplicate_key = value2
'''
expected = {'duplicate_key': 'value2', 'another_key': 'foo'}
self.assertEqual(jc.parsers.kv.parse(data, quiet=True), expected)
if __name__ == '__main__':
unittest.main()

View File

@@ -0,0 +1,35 @@
import os
import unittest
import json
import jc.parsers.update_alt_gs
THIS_DIR = os.path.dirname(os.path.abspath(__file__))
class MyTests(unittest.TestCase):
def setUp(self):
# input
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/generic/update-alternatives-get-selections.out'), 'r', encoding='utf-8') as f:
self.update_alternatives_get_selections = f.read()
# output
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/generic/update-alternatives-get-selections.json'), 'r', encoding='utf-8') as f:
self.update_alternatives_get_selections_json = json.loads(f.read())
def test_update_alt_gs_nodata(self):
"""
Test 'update-alternatives --get-selections' with no data
"""
self.assertEqual(jc.parsers.update_alt_gs.parse('', quiet=True), [])
def test_update_alt_gs(self):
"""
Test 'update-alternatives --get-selections'
"""
self.assertEqual(jc.parsers.update_alt_gs.parse(self.update_alternatives_get_selections, quiet=True), self.update_alternatives_get_selections_json)
if __name__ == '__main__':
unittest.main()

View File

@@ -0,0 +1,48 @@
import os
import unittest
import json
import jc.parsers.update_alt_q
THIS_DIR = os.path.dirname(os.path.abspath(__file__))
class MyTests(unittest.TestCase):
def setUp(self):
# input
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/generic/update-alternatives-query.out'), 'r', encoding='utf-8') as f:
self.update_alternatives_query = f.read()
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/generic/update-alternatives-query2.out'), 'r', encoding='utf-8') as f:
self.update_alternatives_query2 = f.read()
# output
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/generic/update-alternatives-query.json'), 'r', encoding='utf-8') as f:
self.update_alternatives_query_json = json.loads(f.read())
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/generic/update-alternatives-query2.json'), 'r', encoding='utf-8') as f:
self.update_alternatives_query2_json = json.loads(f.read())
def test_update_alt_q_nodata(self):
"""
Test 'update-alternatives --query' with no data
"""
self.assertEqual(jc.parsers.update_alt_q.parse('', quiet=True), {})
def test_update_alt_q(self):
"""
Test 'update-alternatives --query'
"""
self.assertEqual(jc.parsers.update_alt_q.parse(self.update_alternatives_query, quiet=True), self.update_alternatives_query_json)
def test_update_alt_q_no_slaves(self):
"""
Test 'update-alternatives --query' with no slaves in output
"""
self.assertEqual(jc.parsers.update_alt_q.parse(self.update_alternatives_query2, quiet=True), self.update_alternatives_query2_json)
if __name__ == '__main__':
unittest.main()

View File

@@ -30,7 +30,8 @@ class XrandrTests(unittest.TestCase):
"HDMI1 connected (normal left inverted right x axis y axis)",
"VIRTUAL1 disconnected (normal left inverted right x axis y axis)",
"eDP1 connected primary 1920x1080+0+0 (normal left inverted right x axis y axis) 310mm x 170mm",
"eDP-1 connected primary 1920x1080+0+0 (normal left inverted right x axis y axis) 309mm x 174mm"
"eDP-1 connected primary 1920x1080+0+0 (normal left inverted right x axis y axis) 309mm x 174mm",
"HDMI-0 connected 2160x3840+3840+0 right (normal left inverted right x axis y axis) 609mm x 349mm",
]
for device in devices:
self.assertIsNotNone(re.match(_device_pattern, device))
@@ -85,7 +86,7 @@ class XrandrTests(unittest.TestCase):
def test_device(self):
# regex101 sample link for tests/edits https://regex101.com/r/3cHMv3/1
sample = "eDP1 connected primary 1920x1080+0+0 (normal left inverted right x axis y axis) 310mm x 170mm"
sample = "eDP1 connected primary 1920x1080+0+0 left (normal left inverted right x axis y axis) 310mm x 170mm"
actual: Optional[Device] = _parse_device([sample])
expected = {
@@ -98,6 +99,7 @@ class XrandrTests(unittest.TestCase):
"offset_height": 0,
"dimension_width": 310,
"dimension_height": 170,
"rotation": "left",
}
self.assertIsNotNone(actual)
@@ -178,4 +180,4 @@ class XrandrTests(unittest.TestCase):
)
if __name__ == '__main__':
unittest.main()
unittest.main()

View File

@@ -41,6 +41,14 @@ class MyTests(unittest.TestCase):
"""
self.assertEqual(jc.parsers.yaml.parse(self.generic_yaml_istio_sidecar, quiet=True), self.generic_yaml_istio_sidecar_json)
def test_yaml_datetime(self):
"""
Test yaml file with datetime object (should convert to a string)
"""
data = 'deploymentTime: 2022-04-18T11:12:47'
expected = [{"deploymentTime":"2022-04-18T11:12:47"}]
self.assertEqual(jc.parsers.yaml.parse(data, quiet=True), expected)
if __name__ == '__main__':
unittest.main()

View File

@@ -1,11 +1,21 @@
#!/bin/bash
# Update all documentation (README.md, Man page, Doc files)
echo === Building README.md
./readmegen.py && echo "+++ README.md build successful" || echo "--- README.md build failed"
(
echo === Building README.md
./readmegen.py && echo "++++ README.md build successful" || echo "---- README.md build failed"
) &
echo === Building man page
./mangen.py && echo "+++ man page build successful" || echo "--- man page build failed"
(
echo === Building man page
./mangen.py && echo "++++ man page build successful" || echo "---- man page build failed"
) &
echo === Building documentation
./docgen.sh && echo "+++ documentation build successful" || echo "--- documentation build failed"
(
echo === Building documentation
./docgen.sh && echo "++++ documentation build successful" || echo "---- documentation build failed"
) &
wait
echo
echo "All documentation updated"