Skip to content

Commit

Permalink
core: add --stdout mode
Browse files Browse the repository at this point in the history
  • Loading branch information
karlicoss committed Oct 28, 2023
1 parent 1d3b69d commit 0ffe28a
Show file tree
Hide file tree
Showing 2 changed files with 35 additions and 25 deletions.
55 changes: 33 additions & 22 deletions src/orger/org_view.py
Original file line number Diff line number Diff line change
@@ -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...
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -190,51 +198,51 @@ 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,
dry_run=dry_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,
)

Expand All @@ -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')
Expand All @@ -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,
Expand Down
5 changes: 2 additions & 3 deletions src/orger/state.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand All @@ -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)

Expand Down

0 comments on commit 0ffe28a

Please sign in to comment.