From 0ffe28a4706f95cdfbd6f999deabc5f003f0aa98 Mon Sep 17 00:00:00 2001 From: karlicoss Date: Sat, 28 Oct 2023 02:35:02 +0100 Subject: [PATCH] core: add --stdout mode --- src/orger/org_view.py | 55 ++++++++++++++++++++++++++----------------- src/orger/state.py | 5 ++-- 2 files changed, 35 insertions(+), 25 deletions(-) diff --git a/src/orger/org_view.py b/src/orger/org_view.py index fd32e2a..2bcf7f2 100644 --- a/src/orger/org_view.py +++ b/src/orger/org_view.py @@ -1,16 +1,17 @@ #!/usr/bin/env python3 import argparse from argparse import ArgumentParser, Namespace +from collections import Counter import logging import inspect from pathlib import Path from subprocess import check_call -from tempfile import TemporaryDirectory +import sys from typing import List, Tuple, Iterable, Optional, Union, Callable, Dict from .inorganic import OrgNode, TimestampStyle from .state import JsonState -from .atomic_append import PathIsh, atomic_append_check, assert_not_edited +from .atomic_append import atomic_append_check, assert_not_edited from .common import setup_logger, orger_user_dir # TODO tests for determinism? not sure where should they be... @@ -103,22 +104,29 @@ class Mirror(OrgView): @classmethod def main(cls, setup_parser=None) -> None: p = cls.parser() - p.add_argument('--to', type=Path, default=Path(cls.name() + '.org'), help='Filename to output') + og = p.add_mutually_exclusive_group() + og.add_argument('--to', type=Path, default=Path(cls.name() + '.org'), help='Filename to output') + og.add_argument('--stdout', action='store_true', help='pass to print output to stdout, useful for testing/debugging') if setup_parser is not None: setup_parser(p) args = p.parse_args() inst = cls(cmdline_args=args) inst.main_common() - inst._run(to=args.to) + inst._run(to=args.to, stdout=args.stdout) def get_items(self) -> Iterable: raise NotImplementedError - def _run(self, to: Path): + def _run(self, to: Path, stdout: bool) -> None: org_tree = self.make_tree() rtree = org_tree.render(level=0) + if stdout: + print(rtree) + return + + # otherwise output to file assert_not_edited(to) # again, not properly atomic, but hopefully enough # TODO create a github issue, maybe someone comes up with proper way of solving this @@ -190,23 +198,27 @@ class Queue(OrgView): def _run( self, to: Path, + stdout: bool, state_path: Path, init: bool=False, dry_run: bool=False, ) -> None: self.logger.info('Using state file %s', state_path) - if not to.exists() and not init: - err = RuntimeError(f"{to} doesn't exist! Try running with --init") - import sys - if sys.stdin.isatty(): - resp = input(f"{to} doesn't exist. Create empty file? y/n ").strip().lower() - if resp != 'y': + if stdout: + appender = lambda s: sys.stdout.write(s) + else: + appender = lambda s: atomic_append_check(to, s) + + if not to.exists() and not init: + err = RuntimeError(f"{to} doesn't exist! Try running with --init") + if sys.stdin.isatty(): + resp = input(f"{to} doesn't exist. Create empty file? y/n ").strip().lower() + if resp != 'y': + raise err + else: raise err - else: - raise err - state_path.parent.mkdir(parents=True, exist_ok=True) # not sure... state = JsonState( path=state_path, logger=self.logger, @@ -214,27 +226,23 @@ def _run( ) items = list(self.get_items()) - from collections import Counter dups = [k for k, cnt in Counter(i[0] for i in items).items() if cnt > 1] if len(dups) > 0: raise RuntimeError(f'Duplicate items {dups}') if not to.exists(): self.logger.warning("target %s didn't exist, initializing!", to) - atomic_append_check(to, self.file_header + '\n') + appender(self.file_header + '\n') for key, item in items: def action(item=item): # not sure about this newline, but better to have extra whitespace than rely on trailing rendered = '\n' + item.render(level=1) - atomic_append_check( - to, - rendered, - ) + appender(rendered) self.logger.debug('processing %s', key) state.feed( key=key, - value=item, # TODO not sure about this one... perhaps only link? + value=item, # TODO not sure about this one... perhaps only link? action=action, ) @@ -245,7 +253,9 @@ def get_items(self) -> Iterable[OrgWithKey]: def main(cls, setup_parser=None) -> None: default_state = orger_user_dir() / 'states' / (cls.name() + '.state.json') p = cls.parser() - p.add_argument('--to' , type=Path, default=Path(cls.name() + '.org') , help='file where new items are added') + og = p.add_mutually_exclusive_group() + og.add_argument('--to' , type=Path, default=Path(cls.name() + '.org') , help='file where new items are added') + og.add_argument('--stdout', action='store_true', help='pass to print output to stdout, useful for testing/debugging') p.add_argument('--state', type=Path, default=default_state, help='state file for keeping track of handled items') p.add_argument('--init', action='store_true') # todo not sure if I really need it? p.add_argument('--dry-run', action='store_true', help='Run without modifying the state file') @@ -257,6 +267,7 @@ def main(cls, setup_parser=None) -> None: inst.main_common() inst._run( to=args.to, + stdout=args.stdout, state_path=args.state, init=args.init, dry_run=args.dry_run, diff --git a/src/orger/state.py b/src/orger/state.py index 5671bbe..c5f44b2 100644 --- a/src/orger/state.py +++ b/src/orger/state.py @@ -44,6 +44,7 @@ def __setitem__(self, key: str, value: Any) -> None: self.logger.debug('dry run! ignoring %s: %s', key, value) return + self.path.parent.mkdir(parents=True, exist_ok=True) with atomic_write(str(self.path), overwrite=True) as fo: json.dump(current, fo, indent=1, sort_keys=True) @@ -63,9 +64,7 @@ def feed(self, key: str, value: Any, action: Callable[[], None]) -> None: if key in self: self.logger.debug('already handled: %s: %s', key, value) return - self.logger.info('adding %s: %s', key, value) - # TODO not sure about print... - print(f'adding new item {key}: {value}') + self.logger.info('adding new item %s: %s', key, value) action() self[key] = repr(value)