def config(_, unset=False, get=False, all=False): ''' Define, display or remove a global configuration parameter. ''' value = ((_.options.setconfig if not get else (_.options.getconfig if not all else None)) if not unset else _.options.unsetconfig) if value is None and not all: warn("No configuration key provided") return 1 if not unset and not get and "=" not in value: warn( "Configuration entry must be specified in the form <key>=<value>" ) return 2 key, value = safeSplit( value, "=")[:2] if not unset and not get else (value, None) key = wrapExc( lambda: key.lower()) # config keys are normalized to lower case folder, meta = getRoot(_.options, _.args) cfg = Configuration() cfg.load(meta) if get: # get operation if all: for k, v in ((_k, cfg.__dict__[_k]) for _k in cfg.__dict__ if _k != "paths"): warn(f"Configuration entry: {k} = {v}") return 0 elif key in cfg.__dict__: warn(f"Configuration entry: {key} = {cfg.__dict__[key]}") return 0 else: warn(f"Configuration key '{key}' not found") return 3 if '' not in cfg.paths: cfg.paths[''] = dd() # create global section entries = dictGetSet(cfg.paths[''], GLOBAL, []) index = wrapExc(lambda: lindex( [kv.split("=")[0].lower() for kv in entries], key)) # find index for specified key, or None if not unset: # set operation if index is not None: entries[index] = f"{key}={value}" warn(f"Updated configuration entry: {key} = {value}") else: entries.append(f"{key}={value}") warn(f"Added configuration entry: {key} = {value}") cfg.__dict__[key] = value if value.lower() not in ( "true", "false") else value.lower() == "true" else: # unset operation if index is not None: del entries[index] warn(f"Removed configuration entry for key '{key}'") else: warn("Configuration entry not found, nothing to do") try: del cfg.__dict__[key] # remove in-memory global configuration except: pass if not _.options.simulate: cfg.store(meta) return 0
def getRoot(options, args): ''' Determine tagsPlorer repository root folder by checking program arguments with some fall-back logic. returns 2-tuple(root folder absolute path, index/config file folder absolute path), depending on options >>> class O: ... def __init__(_): _.index = _.root = None # options object >>> os.chdir(os.path.join(os.path.dirname(os.path.abspath(__file__)), os.pardir, "_test-data")) # change into folder that contains .tagsplorer.cfg >>> o = O(); rela = lambda a, b: (os.path.relpath(a, start = os.getcwd()), os.path.relpath(b, start = os.getcwd())) # helper to pass with any current folder >>> rela(*getRoot(o, [])) # no options nor arguments: root folder == index folder ('.', '.') >>> o.root = "./x"; rela(*getRoot(o, [])) # given a root: root = index ('x', 'x') >>> o.index = "./y"; rela(*getRoot(o, [])) # given both ('x', 'y') >>> o.root = None; o.index = "abc"; rela(*getRoot(o, [])) ('abc', 'abc') ''' if options.index: # given folder to place the index if options.root is None: options.root = str(options.index) # copy by value return (os.path.abspath(options.root), os.path.abspath(options.index)) # determine root folder by finding the config/index files folder = options.root # None if not defined if folder is None: folder = wrapExc( lambda: os.path.relpath(os.path.dirname(findRootFolder(CONFIG)))) if folder is None: folder = os.curdir # fallback root, meta = os.path.abspath(folder), os.path.abspath( options.index if options.index else folder) debug(f"Root and index folders found at {root} {meta}") return root, meta
def tmp2(): i = lib.Indexer(REPO) i.load(os.path.join(REPO, INDEX), ignore_skew = True, recreate_index = False) print(utils.wrapExc(lambda: set(i.getPaths(i.tagdir2paths[i.tagdirs.index("a")], {})), lambda: set())) # print output is captured
from tagsplorer.utils import caseCompareKey, casefilter, dd, dictGetSet, isDir, isGlob, isUnderRoot, lindex, normalizer, pathNorm, removeTagPrefixes, safeRSplit, safeSplit, sjoin, splitByPredicate, splitTags, wrapExc, xany from tagsplorer import lib, simfs, utils # for setting the log level dynamically STREAM = sys.stdout if '--stdout' in sys.argv else sys.stderr logging.basicConfig( level=logging.DEBUG if '-V' in sys.argv or '--debug' in sys.argv or os.environ.get("DEBUG", "False").lower() == "true" else ( logging.INFO if '-v' in sys.argv or '--verbose' in sys.argv or os.environ.get("VERBOSE", "False").lower() == "true" else logging.WARNING ), # must be set here to already limit writing to info and debug (e.g. "Started at...") stream=STREAM, # log to stderr, write results to stdout format= '%(asctime)-8s.%(msecs)03d %(levelname)-4s %(module)s:%(funcName)s:%(lineno)d | %(message)s', datefmt='%H:%M:%S') wrapExc(lambda: sys.argv.remove('--stdout')) # remove if present _log = logging.getLogger(__name__) def log(func): return (lambda *s: func( sjoin([_() if callable(_) else _ for _ in s]), **({ "stacklevel": 2 } if sys.version_info >= (3, 8) else {}))) debug, info, warn, error = log(_log.debug), log(_log.info), log( _log.warning), log(_log.error) with open(os.path.join(os.path.dirname(__file__), 'VERSION'),
def __exists(path): return wrapExc(lambda: _exists(_realPath(path)), False) # also for file handles
_.assertTrue(os.path.exists("_test-data/tmp2")) os.rmdir("_test-data/tmp2") _.assertFalse(os.path.exists("./_test-data/tmp2")) def load_tests(loader, tests, ignore): ''' Queried by unittest. ''' tests.addTests(doctest.DocTestSuite("tagsplorer.simfs")) return tests else: SIMFS = False if __name__ == '__main__': logging.basicConfig( level=logging.DEBUG if os.environ.get( "DEBUG", "False").lower() == "true" else logging.INFO, stream=sys.stderr, format= "%(asctime)-23s %(levelname)-8s %(name)s:%(lineno)d | %(message)s") if sys.platform == 'win32': print( "Testing on Windows makes no sense. This is a Windows file system simulator!" ) exit(1) os.chdir(os.path.dirname(os.path.dirname( os.path.abspath(__file__)))) # main repo folder unittest.main() # warnings = "ignore") for file in ("_X.x", "_x.X"): wrapExc(lambda: _unlink(file))