Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package python-pyaml for openSUSE:Factory checked in at 2026-03-09 16:11:40 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-pyaml (Old) and /work/SRC/openSUSE:Factory/.python-pyaml.new.8177 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-pyaml" Mon Mar 9 16:11:40 2026 rev:17 rq:1337530 version:26.2.1 Changes: -------- --- /work/SRC/openSUSE:Factory/python-pyaml/python-pyaml.changes 2025-07-14 10:58:09.584693245 +0200 +++ /work/SRC/openSUSE:Factory/.python-pyaml.new.8177/python-pyaml.changes 2026-03-09 16:12:20.767416907 +0100 @@ -1,0 +2,7 @@ +Sun Mar 8 21:08:28 UTC 2026 - Dirk Müller <[email protected]> + +- update to 26.2.1: + * cli: add -o/--out-file option + * cli: allow reading/replacing multiple input files + +------------------------------------------------------------------- Old: ---- pyaml-25.7.0.tar.gz New: ---- pyaml-26.2.1.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-pyaml.spec ++++++ --- /var/tmp/diff_new_pack.UMpksY/_old 2026-03-09 16:12:23.311521669 +0100 +++ /var/tmp/diff_new_pack.UMpksY/_new 2026-03-09 16:12:23.311521669 +0100 @@ -1,7 +1,7 @@ # # spec file for package python-pyaml # -# Copyright (c) 2025 SUSE LLC +# Copyright (c) 2026 SUSE LLC and contributors # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -18,7 +18,7 @@ %{?sle15_python_module_pythons} Name: python-pyaml -Version: 25.7.0 +Version: 26.2.1 Release: 0 Summary: Python module to produce formatted YAML-serialized data License: WTFPL ++++++ pyaml-25.7.0.tar.gz -> pyaml-26.2.1.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyaml-25.7.0/PKG-INFO new/pyaml-26.2.1/PKG-INFO --- old/pyaml-25.7.0/PKG-INFO 2025-07-10 20:44:48.431457000 +0200 +++ new/pyaml-26.2.1/PKG-INFO 2026-02-06 14:49:27.148404400 +0100 @@ -1,6 +1,6 @@ Metadata-Version: 2.4 Name: pyaml -Version: 25.7.0 +Version: 26.2.1 Summary: PyYAML-based module to produce a bit more pretty and readable YAML-serialized data Home-page: https://github.com/mk-fg/pretty-yaml Author: Mike Kazantsev diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyaml-25.7.0/pyaml/__init__.py new/pyaml-26.2.1/pyaml/__init__.py --- old/pyaml-25.7.0/pyaml/__init__.py 2025-06-19 22:37:36.000000000 +0200 +++ new/pyaml-26.2.1/pyaml/__init__.py 2026-01-28 08:28:15.000000000 +0100 @@ -53,9 +53,9 @@ self.anchor_node(key) self.anchor_node(value, hints=hints+[key]) - def serialize_node(self, node, parent, index): - if self.pyaml_force_embed: self.anchors[node] = self.serialized_nodes.clear() - return super().serialize_node(node, parent, index) + def ignore_aliases(self, data): + if self.pyaml_force_embed: return True + return super().ignore_aliases(data) def expect_block_sequence(self): self.increase_indent(flow=False, indentless=False) @@ -76,7 +76,7 @@ if self.analysis: self.analysis.allow_flow_plain = False return res - def choose_scalar_style(self, _re1=re.compile(r':(\s|$)')): + def choose_scalar_style(self, _re1=re.compile(r':(\s|\Z)')): if self.states[-1] == self.expect_block_mapping_simple_value: # Mapping keys - disable overriding string style, strip comments if self.pyaml_string_val_style: self.event.style = 'plain' @@ -140,7 +140,7 @@ if self.pyaml_repr_unknown: # repr value as a short oneliner if isinstance(n := self.pyaml_repr_unknown, bool): n = 50 if len(s := repr(data).replace('\n', '⏎')) > n + 10: - if (m := re.search(r' at (0x[0-9a-f]+>)$', s)) and n > len(m[0]): + if (m := re.search(r' at (0x[0-9a-f]+>)\Z', s)) and n > len(m[0]): s = s[:n-len(m[0])] + f' ~[{n:,d}/{len(s):,d}]~ ' + m[1] else: s = s[:n] + f' ...[{n:,d}/{len(s):,d}]' cls, node = data.__class__, self.represent_data(s) @@ -201,7 +201,7 @@ blocks.append((a, n, _add_vspacing(block)[a_seq:])) ind_re_sub = None if ind_re.match(line): item_lines.append(n) - if m := re.match(r'( *)(- )?\S.*:(\s|$)', line): + if m := re.match(r'( *)(- )?\S.*:(\s|\Z)', line): a, a_seq, ind_re_sub = n+1, bool(m[2]), re.compile(m[1] + ' ') if ( split_items := len(lines) > split_lines and len(item_lines) > split_count and (oneline_split or has_sub) ): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyaml-25.7.0/pyaml/cli.py new/pyaml-26.2.1/pyaml/cli.py --- old/pyaml-25.7.0/pyaml/cli.py 2024-07-18 15:00:40.000000000 +0200 +++ new/pyaml-26.2.1/pyaml/cli.py 2026-02-06 14:34:30.000000000 +0100 @@ -5,7 +5,7 @@ @contextlib.contextmanager def safe_replacement(path, *open_args, mode=None, xattrs=None, **open_kws): 'Context to atomically create/replace file-path in-place unless errors are raised' - path, xattrs = str(path), None + path = str(path) if mode is None: try: mode = stat.S_IMODE(os.lstat(path).st_mode) except FileNotFoundError: pass @@ -54,10 +54,13 @@ parser = argparse.ArgumentParser( formatter_class=argparse.RawTextHelpFormatter, description='Process and dump prettified YAML to stdout.') - parser.add_argument('path', nargs='?', metavar='path', - help='Path to YAML to read (default: use stdin).') + parser.add_argument('path', nargs='*', metavar='path', default=list(), help=dd(''' + Path(s) to YAML to read (default/empty: use stdin). + With multiple paths, will output YAML documents from each one in the same order.''')) parser.add_argument('-r', '--replace', action='store_true', - help='Replace specified path with prettified version in-place.') + help='Replace specified YAML file(s) with prettified version in-place.') + parser.add_argument('-o', '--out-file', metavar='file-path', + help='Write output to specified file instead of standard output.') parser.add_argument('-w', '--width', type=int, metavar='chars', help=dd(''' Max line width hint to pass to pyyaml for the dump. Only used to format scalars and collections (e.g. lists).''')) @@ -80,15 +83,6 @@ help='Disable sanity-check on the output and suppress stderr warnings.') opts = parser.parse_args(sys.argv[1:] if argv is None else argv) - if opts.replace and not opts.path: - parser.error('-r/--replace option can only be used with a file path, not stdin') - - src = open(opts.path) if opts.path else stdin - try: - data = list( yaml.safe_load_all(src) if not opts.lines else - (yaml.safe_load(chunk) for chunk in file_line_iter(src) if chunk.strip()) ) - finally: src.close() - pyaml_kwargs = dict() if opts.width: pyaml_kwargs['width'] = opts.width if vspacing := opts.vspacing: @@ -104,24 +98,41 @@ if count: vspacing['split_count'] = int(count.strip()) if vspacing: pyaml_kwargs['vspacing'] = vspacing - if len(data) > 1: ys = pyaml.dump_all(data, **pyaml_kwargs) - else: ys = pyaml.dump(data[0], **pyaml_kwargs) # avoids leading "---" + if not all(paths := opts.path or ['']) and opts.replace: + parser.error('-r/--replace option can only be used with a file path, not stdin') + if opts.out_file and opts.replace: + parser.error('-r/--replace and -o/--out-file options cannot be used at the same time') + with contextlib.ExitStack() as srcs: + data = list([p, srcs.enter_context(open(p)) if p else stdin] for p in (paths or [''])) + for d in data: d[1] = list( yaml.safe_load_all(d[1]) if not opts.lines else + (yaml.safe_load(chunk) for chunk in file_line_iter(d[1]) if chunk.strip()) ) + for d in data: d.append( + pyaml.dump(d[1][0], **pyaml_kwargs) # avoid leading "---" if possible + if len(data) == len(d[1]) == 1 else pyaml.dump_all(d[1], **pyaml_kwargs) ) if not opts.quiet: try: - data_chk = list(yaml.safe_load_all(ys)) - try: data_hash = json.dumps(data, sort_keys=True) - except: pass # too complex for checking with json - else: - if json.dumps(data_chk, sort_keys=True) != data_hash: - raise AssertionError('Data from before/after pyaml does not match') + for p, d, ys in data: + data_chk = list(yaml.safe_load_all(ys)) + try: data_hash = json.dumps(d, sort_keys=True) + except: pass # too complex for checking with json + else: + if json.dumps(data_chk, sort_keys=True) != data_hash: + raise AssertionError('Data from before/after pyaml does not match') except Exception as err: p_err = lambda *a,**kw: print(*a, **kw, file=stderr, flush=True) - p_err( 'WARNING: Failed to parse produced YAML' - ' output back to data, it is likely too complicated for pyaml' ) + p_err( 'WARNING: Failed to parse produced YAML output for' + f' <{p or "stdin"}> back to same data, it is likely too complicated for pyaml' ) err = f'[{err.__class__.__name__}] {err}' p_err(' raised error: ' + ' // '.join(map(str.strip, err.split('\n')))) - if opts.replace: - with safe_replacement(opts.path) as tmp: tmp.write(ys) - else: stdout.write(ys) + with contextlib.ExitStack() as dsts: + if opts.replace: + for d in data: d[0] = dsts.enter_context(safe_replacement(d[0])) + elif p := opts.out_file: + dst = dsts.enter_context( safe_replacement(p) if not + any(p.startswith(pre) for pre in ['/dev/', '/proc/']) else open(p, 'w') ) + for d in data: d[0] = dst + else: + for d in data: d[0] = stdout + for dst, d, ys in data: dst.write(ys) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyaml-25.7.0/pyaml/tests/test_cli.py new/pyaml-26.2.1/pyaml/tests/test_cli.py --- old/pyaml-25.7.0/pyaml/tests/test_cli.py 2024-07-18 15:00:40.000000000 +0200 +++ new/pyaml-26.2.1/pyaml/tests/test_cli.py 2026-02-06 14:47:05.000000000 +0100 @@ -134,7 +134,7 @@ err.seek(0); err.truncate() with tempfile.NamedTemporaryFile(prefix='.pyaml.test.') as tmp: - d_json, d_yaml = json.dumps(d).encode(), pyaml.dump(d, bytes) + d_json = json.dumps(d).encode() tmp.write(d_json); tmp.flush() os.fchmod(tmp.fileno(), 0o1510) stat_tmp = os.fstat(tmp.fileno()) @@ -228,4 +228,69 @@ self.assertNotIn('doc2_val', xd3) self.assertNotIn('---', out.getvalue()) + def test_multiple_args(self): + ds, out, err = [data.copy(), dict(data, diff=123)], io.StringIO(), io.StringIO() + with tempfile.NamedTemporaryFile(prefix='.pyaml.test.') as tmp1, \ + tempfile.NamedTemporaryFile(prefix='.pyaml.test.') as tmp2: + tmp_files, ds_json = [tmp1, tmp2], list(json.dumps(d).encode() for d in ds) + for tmp, d_json in zip(tmp_files, ds_json): tmp.write(d_json); tmp.flush() + + with self.assertRaises(SystemExit): + sys.stdout, sys.stderr, sys_out, sys_err = out, err, sys.stdout, sys.stderr + try: pyaml.cli.main( + argv=['-r', tmp1.name, '', tmp2.name], # empty arg = stdin + stdin=io.StringIO(), stdout=out, stderr=err ) + finally: sys.stdout, sys.stderr = sys_out, sys_err + for tmp, d_json in zip(tmp_files, ds_json): + tmp.seek(0); self.assertEqual(tmp.read().decode(), d_json) + self.assertEqual(out.getvalue(), '') + self.assertGreater(len(err.getvalue()), 50) + err.seek(0); err.truncate() + + pyaml.cli.main( argv=['-r', tmp1.name, tmp2.name], + stdin=io.StringIO(), stdout=out, stderr=err ) + for tmp, d, d_json in zip(tmp_files, ds, ds_json): + tmp.seek(0); d_yaml = tmp.read().decode() + self.assertNotEqual(d_json, d_yaml) + self.assertEqual(self.data_hash(d), self.data_hash(yaml.safe_load(d_yaml))) + self.assertEqual(out.getvalue(), '') + self.assertEqual(err.getvalue(), '') + + ys_stdin = yaml.safe_dump_all( + d_stdin := [dict(ds[0], diff2=dict(a=['xyz'])), 'abcde', [1,2,3]] ) + pyaml.cli.main( argv=[tmp2.name, '', tmp1.name], + stdin=io.StringIO(ys_stdin), stdout=out, stderr=err ) + self.assertEqual(list(yaml.safe_load_all(out.getvalue())), [ds[1], *d_stdin, ds[0]]) + + def test_out_file(self): + d, out, err = data.copy(), io.StringIO(), io.StringIO() + ys, ys_pyaml = yaml.safe_dump(d), pyaml.dump(d) + with tempfile.NamedTemporaryFile(prefix='.pyaml.test.') as tmp: + tmp.write(d_tmp := b'== some earlier data ==') + + with self.assertRaises(SystemExit): + sys.stdout, sys.stderr, sys_out, sys_err = out, err, sys.stdout, sys.stderr + try: + pyaml.cli.main( argv=['-r', '-o', tmp.name], + stdin=io.StringIO(), stdout=out, stderr=err ) + finally: sys.stdout, sys.stderr = sys_out, sys_err + self.assertEqual(out.getvalue(), '') + self.assertGreater(len(err.getvalue()), 50) + err.seek(0); err.truncate() + + st = os.stat(p := '/dev/null'); st_id = st.st_dev, st.st_ino + pyaml.cli.main( argv=['-o', p], # shouldn't replace dev node + stdin=io.StringIO(ys), stdout=out, stderr=err ) + self.assertEqual(out.getvalue(), '') + self.assertEqual(err.getvalue(), '') + tmp.seek(0); self.assertEqual(tmp.read(), d_tmp) + st = os.stat(p); self.assertEqual((st.st_dev, st.st_ino), st_id) + + pyaml.cli.main( argv=['-o', tmp.name], + stdin=io.StringIO(ys), stdout=out, stderr=err ) + self.assertEqual(out.getvalue(), '') + self.assertEqual(err.getvalue(), '') + tmp.seek(0); self.assertEqual(tmp.read(), d_tmp) + with open(tmp.name) as tmp_repl: self.assertEqual(tmp_repl.read(), ys_pyaml) + if __name__ == '__main__': unittest.main() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyaml-25.7.0/pyaml.egg-info/PKG-INFO new/pyaml-26.2.1/pyaml.egg-info/PKG-INFO --- old/pyaml-25.7.0/pyaml.egg-info/PKG-INFO 2025-07-10 20:44:48.000000000 +0200 +++ new/pyaml-26.2.1/pyaml.egg-info/PKG-INFO 2026-02-06 14:49:27.000000000 +0100 @@ -1,6 +1,6 @@ Metadata-Version: 2.4 Name: pyaml -Version: 25.7.0 +Version: 26.2.1 Summary: PyYAML-based module to produce a bit more pretty and readable YAML-serialized data Home-page: https://github.com/mk-fg/pretty-yaml Author: Mike Kazantsev diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyaml-25.7.0/pyproject.toml new/pyaml-26.2.1/pyproject.toml --- old/pyaml-25.7.0/pyproject.toml 2025-07-10 20:43:58.000000000 +0200 +++ new/pyaml-26.2.1/pyproject.toml 2026-02-06 14:48:21.000000000 +0100 @@ -1,7 +1,7 @@ [project] name = "pyaml" -version = "25.7.0" +version = "26.2.1" description = "PyYAML-based module to produce a bit more pretty and readable YAML-serialized data" authors = [{name="Mike Kazantsev", email="[email protected]"}]
