Skip to content

Commit

Permalink
sphobjinv-textconv
Browse files Browse the repository at this point in the history
- feat: add sphobjinv-textconv entrypoint (bskinn#295)
  • Loading branch information
msftcangoblowm committed Dec 12, 2024
1 parent ff9151a commit c204d4b
Show file tree
Hide file tree
Showing 17 changed files with 1,391 additions and 23 deletions.
1 change: 1 addition & 0 deletions .coveragerc
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ source =
omit =
# Don't worry about covering vendored libraries
src/sphobjinv/_vendored/*
setup.py

[report]
exclude_lines =
Expand Down
123 changes: 112 additions & 11 deletions conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
"""


import logging
import os.path as osp
import platform
import re
Expand Down Expand Up @@ -82,6 +82,51 @@ def res_dec(res_path, misc_info):
return res_path / (misc_info.FNames.RES.value + misc_info.Extensions.DEC.value)


@pytest.fixture(scope="session")
def res_cmp_plus_one_line(res_path, misc_info):
"""res_cmp with a line appended. Overwrites objects.inv file."""

def func(path_cwd):
"""Overwrite objects.inv file. New objects.inv contains one additional line.
Parameters
----------
path_cwd
|Path| -- test sessions current working directory
"""
logger = logging.getLogger()

# src
str_postfix = "_plus_one_entry"
fname = (
f"{misc_info.FNames.RES.value}{str_postfix}{misc_info.Extensions.CMP.value}"
)
path_f_src = res_path / fname
reason = f"source file not found src {path_f_src}"
assert path_f_src.is_file() and path_f_src.exists(), reason

# dst
fname_dst = f"{misc_info.FNames.INIT.value}{misc_info.Extensions.CMP.value}"
path_f_dst = path_cwd / fname_dst
reason = f"dest file not found src {path_f_src} dest {path_f_dst}"
assert path_f_dst.is_file() and path_f_dst.exists(), reason

# file sizes differ
objects_inv_size_existing = path_f_dst.stat().st_size
objects_inv_size_new = path_f_src.stat().st_size
reason = f"file sizes do not differ src {path_f_src} dest {path_f_dst}"
assert objects_inv_size_new != objects_inv_size_existing, reason

msg_info = f"copy {path_f_src} --> {path_f_dst}"
logger.info(msg_info)

shutil.copy2(str(path_f_src), str(path_f_dst))

return func


@pytest.fixture(scope="session")
def misc_info(res_path):
"""Supply Info object with various test-relevant content."""
Expand Down Expand Up @@ -152,19 +197,13 @@ def scratch_path(tmp_path, res_path, misc_info, is_win, unix2dos):
# With the conversion of resources/objects_attrs.txt to Unix EOLs in order to
# provide for a Unix-testable sdist, on Windows systems this resource needs
# to be converted to DOS EOLs for consistency.
if is_win:
if is_win: # pragma: no cover
win_path = tmp_path / f"{scr_base}{misc_info.Extensions.DEC.value}"
win_path.write_bytes(unix2dos(win_path.read_bytes()))

yield tmp_path


@pytest.fixture(scope="session")
def ensure_doc_scratch():
"""Ensure doc/scratch dir exists, for README shell examples."""
Path("doc", "scratch").mkdir(parents=True, exist_ok=True)


@pytest.fixture(scope="session")
def bytes_txt(misc_info, res_path):
"""Load and return the contents of the example objects_attrs.txt as bytes."""
Expand Down Expand Up @@ -212,7 +251,7 @@ def func(path):
"""Perform the 'live' inventory load test."""
try:
sphinx_ifile_load(path)
except Exception as e: # noqa: PIE786
except Exception as e: # noqa: PIE786 # pragma: no cover
# An exception here is a failing test, not a test error.
pytest.fail(e)

Expand Down Expand Up @@ -252,7 +291,7 @@ def func(arglist, *, expect=0): # , suffix=None):
except SystemExit as e:
retcode = e.args[0]
ok = True
else:
else: # pragma: no cover
ok = False

# Do all pytesty stuff outside monkeypatch context
Expand All @@ -264,6 +303,68 @@ def func(arglist, *, expect=0): # , suffix=None):
return func


@pytest.fixture() # Must be function scope since uses monkeypatch
def run_cmdline_textconv(monkeypatch):
"""Return function to perform command line exit code test."""
from sphobjinv.cli.core_textconv import main as main_textconv

def func(arglist, *, expect=0): # , suffix=None):
"""Perform the CLI exit-code test."""

# Assemble execution arguments
runargs = ["sphobjinv-textconv"]
runargs.extend(str(a) for a in arglist)

# Mock sys.argv, run main, and restore sys.argv
with monkeypatch.context() as m:
m.setattr(sys, "argv", runargs)

try:
main_textconv()
except SystemExit as e:
retcode = e.args[0]
ok = True
else: # pragma: no cover
ok = False

# Do all pytesty stuff outside monkeypatch context
assert ok, "SystemExit not raised on termination."

# Test that execution completed w/indicated exit code
assert retcode == expect, runargs

return func


@pytest.fixture() # Must be function scope since uses monkeypatch
def run_cmdline_no_checks(monkeypatch):
"""Return function to perform command line. So as to debug issues no tests."""
from sphobjinv.cli.core_textconv import main as main_textconv

def func(arglist, *, prog="sphobjinv-textconv"):
"""Perform the CLI exit-code test."""

# Assemble execution arguments
runargs = [prog]
runargs.extend(str(a) for a in arglist)

# Mock sys.argv, run main, and restore sys.argv
with monkeypatch.context() as m:
m.setattr(sys, "argv", runargs)

try:
main_textconv()
except SystemExit as e:
retcode = e.args[0]
is_system_exit = True
else: # pragma: no cover
is_system_exit = False

return retcode, is_system_exit

return func


@pytest.fixture(scope="session")
def decomp_cmp_test(misc_info, is_win, unix2dos):
"""Return function to confirm a decompressed file is identical to resource."""
Expand All @@ -274,7 +375,7 @@ def func(path):
res_bytes = Path(misc_info.res_decomp_path).read_bytes()
tgt_bytes = Path(path).read_bytes() # .replace(b"\r\n", b"\n")

if is_win:
if is_win: # pragma: no cover
# Have to explicitly convert these newlines, now that the
# tests/resource/objects_attrs.txt file is marked 'binary' in
# .gitattributes
Expand Down
166 changes: 166 additions & 0 deletions doc/source/cli/git_diff.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
.. Description of configure git diff support for inventory files
Integration -- git diff
========================

.. program:: git diff

|soi-textconv| converts .inv files to plain text sending the
output to |stdout|.

.. code-block:: shell
sphobjinv-textconv objects.inv
Which is equivalent to

.. code-block:: shell
sphobjinv convert plain objects.inv -
Convenience aside, why the redundant CLI command, |soi-textconv|?

To compare changes to a |objects.inv| file, :code:`git diff` won't
produce a useful result without configuration. And git only accepts a
CLI command with:

- one input, the INFILE path

- sends output to |stdout|

Usage
------

Initialize git
"""""""""""""""

.. code-block:: shell
git init
git config user.email [email protected]
git config user.name "a test"
Configure git
""""""""""""""

``git diff`` is really useful, so it's time to configure git

There is no CLI command to configure git for us.

In ``.git/config`` (or $HOME/.config/git/config) append,

.. code-block:: text
[diff "inv"]
textconv = [absolute path to venv bin folder]/sphobjinv-textconv
Note has one tab, not whitespace(s)

In ``.gitattributes`` append,

.. code-block:: text
*.inv binary diff=inv
Example
--------

Make one commit
""""""""""""""""

Commit these files:

- objects_attrs.inv

- objects_attrs.txt

- .gitattributes

.. code-block:: shell
git add .
git commit --no-verify --no-gpg-sign -m "test textconv"
Make a change to ``objects_attrs.inv``
"""""""""""""""""""""""""""""""""""""""

By shell

.. code-block:: shell
URL="https://github.com/bskinn/sphobjinv/raw/main/tests/resource/objects_attrs.inv"
wget "$URL"
sphobjinv convert plain -qu "$URL" objects_attrs.txt
export APPEND_THIS="attrs.validators.set_cheat_mode py:function 1 api.html#$ -"
echo "$APPEND_THIS" >> objects_attrs.txt
sphobjinv convert zlib -qu objects_attrs.txt objects_attrs.inv
By python code

.. versionadded:: 2.4.0
Append a line to .inv (compressed) inventory

.. doctest:: append_a_line

>>> from pathlib import Path
>>> from sphobjinv import DataObjStr
>>> from sphobjinv.cli.load import import_infile
>>> from sphobjinv.cli.write import write_plaintext
>>>
>>> remote_url = (
... "https://github.com/bskinn/sphobjinv/"
... "raw/main/tests/resource/objects_attrs.inv"
... )
>>> cli_run(f'sphobjinv convert plain -qu {remote_url} objects_attrs.txt')
<BLANKLINE>
>>> path_dst_dec = Path('objects_attrs.txt')
>>> path_dst_cmp = Path('objects_attrs.inv')
>>> dst_dec_path = str(path_dst_dec)
>>> path_dst_dec.is_file()
True
>>> inv_0 = import_infile(dst_dec_path)
>>> obj_datum = DataObjStr(
... name="attrs.validators.set_cheat_mode",
... domain="py",
... role="function",
... priority="1",
... uri="api.html#$",
... dispname="-",
... )
>>> inv_0.objects.append(obj_datum)
>>> write_plaintext(inv_0, dst_dec_path)
>>> cli_run('sphobjinv convert -q zlib objects_attrs.txt objects_attrs.inv')
<BLANKLINE>
>>> path_dst_cmp.is_file()
True

Show the diff
""""""""""""""

To see the changes to objects_attrs.inv

.. code-block:: shell
git diff HEAD objects_attrs.inv 2>/dev/null
Without |soi-textconv|, *These two binary files differ*

With |soi-textconv| configured

.. code-block:: text
diff --git a/objects.inv b/objects.inv
index 85189bd..65cc567 100644
--- a/objects.inv
+++ b/objects.inv
@@ -131,4 +131,5 @@ types std:doc -1 types.html Type Annotations
validators std:label -1 init.html#$ Validators
version-info std:label -1 api.html#$ -
why std:doc -1 why.html Why not…
+attrs.validators.set_cheat_mode py:function 1 api.html#$ -
The last line contains <whitespace><newline> rather than <newline>

The 2nd line changes every time

:code:`2>/dev/null` means suppress |stderr|
7 changes: 7 additions & 0 deletions doc/source/cli/implementation/core-textconv.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
.. Module API page for cli/core_textconv.py
sphobjinv.cli.core_textconv
===========================

.. automodule:: sphobjinv.cli.core_textconv
:members:
1 change: 1 addition & 0 deletions doc/source/cli/implementation/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ sphobjinv.cli (non-API)

convert
core
core-textconv
load
parser
paths
Expand Down
Loading

0 comments on commit c204d4b

Please sign in to comment.