diff --git a/CHANGES.rst b/CHANGES.rst index f677b6e..c0c49e8 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -7,7 +7,7 @@ Version 2.6.2 (in development) Relax `models.IsoRetirement.remedy` constraint: nullable except for `reason='split'`. -Drop Python 3.7 support. +Drop Python 3.7 support. Use PEP 570 positional-only parameters. Version 2.6.1 diff --git a/tests/helpers.py b/tests/helpers.py index 27e5114..7ff34c5 100644 --- a/tests/helpers.py +++ b/tests/helpers.py @@ -3,13 +3,13 @@ MB = 2**20 -def pairwise(iterable): +def pairwise(iterable, /): a, b = itertools.tee(iterable) next(b, None) return zip(a, b) -def get_assert_head(items, *, n): +def get_assert_head(items, /, *, n): head = list(itertools.islice(items, n)) assert head @@ -18,12 +18,12 @@ def get_assert_head(items, *, n): return head -def assert_nonempty_string(obj): +def assert_nonempty_string(obj, /): assert obj is not None assert isinstance(obj, str) -def assert_nonempty_string_tuple(obj): +def assert_nonempty_string_tuple(obj, /): assert obj is not None assert isinstance(obj, tuple) assert all(isinstance(o, str) for o in obj) @@ -31,20 +31,20 @@ def assert_nonempty_string_tuple(obj): assert all(obj) -def assert_nonempty_dict(obj): +def assert_nonempty_dict(obj, /): assert obj is not None assert isinstance(obj, dict) assert obj -def assert_file_size_between(path, min, max, *, unit=MB): +def assert_file_size_between(path, /, min, max, *, unit=MB): assert path is not None assert path.exists() assert path.is_file() assert min * unit <= path.stat().st_size <= max * unit -def assert_valid_languoids(items, *, n): +def assert_valid_languoids(items, /, *, n): for path, languoid in get_assert_head(items, n=n): assert_nonempty_string_tuple(path) assert_nonempty_dict(languoid) diff --git a/treedb/_globals.py b/treedb/_globals.py index 2a2117a..0a93a3d 100644 --- a/treedb/_globals.py +++ b/treedb/_globals.py @@ -60,8 +60,8 @@ RecordType = typing.Mapping[str, typing.Mapping[str, RecordValueType]] -def filepath_tuple(file_path: str, - *, sep=FILE_PATH_SEP) -> typing.Tuple[str]: +def filepath_tuple(file_path: str, /, *, + sep=FILE_PATH_SEP) -> typing.Tuple[str]: path_parts = file_path.split(sep) return tuple(path_parts) @@ -78,7 +78,7 @@ class RecordItem(typing.NamedTuple): record: RecordType @classmethod - def from_filepath_record(cls, file_path: str, languoid): + def from_filepath_record(cls, file_path: str, languoid, /): return cls(filepath_tuple(file_path), languoid) @@ -121,5 +121,5 @@ class LanguoidItem(typing.NamedTuple): languoid: LanguoidType @classmethod - def from_filepath_languoid(cls, file_path: str, languoid): + def from_filepath_languoid(cls, file_path: str, languoid, /): return cls(filepath_tuple(file_path), languoid) diff --git a/treedb/_proxies.py b/treedb/_proxies.py index 1137eb2..80f541e 100644 --- a/treedb/_proxies.py +++ b/treedb/_proxies.py @@ -37,7 +37,7 @@ class PathProxy(Proxy): . """ - def __init__(self, path=None): + def __init__(self, path=None, /): self.path = path def __fspath__(self): @@ -78,15 +78,15 @@ class EngineProxy(Proxy, sa.engine.Engine): >>> EngineProxy(future=True) """ - def __init__(self, engine=None, *, future): + def __init__(self, engine=None, /, *, future): self.engine = engine self.future = future - def _create_engine(self, url): + def _create_engine(self, url, /): log.debug('sqlalchemy.create_engine(%r)', url) self.engine = sa.create_engine(url, future=self.future) - def connect(self, close_with_result=False, **kwargs): + def connect(self, *, close_with_result: bool = False, **kwargs): return self.engine.connect(**kwargs) def dispose(self): @@ -157,7 +157,7 @@ def __repr__(self): f' filename={name!r}{parent}' f' size={self.file_size()!r}>') - def file_with_suffix(self, suffix): + def file_with_suffix(self, suffix, /): if self.file is None: name = f'{self.memory_write_path.name}{suffix}' return self.memory_write_path.with_name(name) @@ -170,7 +170,7 @@ def file_mtime(self): return (datetime.datetime.fromtimestamp(self.file.stat().st_mtime) if self.file_exists() else None) - def file_size(self, as_megabytes=False): + def file_size(self, *, as_megabytes: bool = False): if self.file_exists(): result = self.file.stat().st_size if as_megabytes: diff --git a/treedb/_tools.py b/treedb/_tools.py index 4155a8c..6a75493 100644 --- a/treedb/_tools.py +++ b/treedb/_tools.py @@ -48,7 +48,7 @@ log = logging.getLogger(__name__) -def uniqued(iterable): +def uniqued(iterable, /): """Return list of unique hashable elements preserving order. >>> uniqued('spamham') @@ -58,10 +58,10 @@ def uniqued(iterable): return [i for i in iterable if i not in seen and not seen.add(i)] -def next_count(start: int = 0, step: int = 1): +def next_count(*, start: int = 0, step: int = 1): """Return a callable returning descending ints. - >>> nxt = next_count(1) + >>> nxt = next_count(start=1) >>> nxt() 1 @@ -115,8 +115,8 @@ def groupby_attrgetter(*attrnames): return functools.partial(itertools.groupby, key=key) -def islice_limit(iterable, - *, limit: typing.Optional[int] = None, +def islice_limit(iterable, /, *, + limit: typing.Optional[int] = None, offset: typing.Optional[int] = 0): """Return a slice from iterable applying limit and offset. @@ -142,7 +142,7 @@ def islice_limit(iterable, return iterable -def iterslices(iterable, *, size: int): +def iterslices(iterable, /, *, size: int): """Yield iterable in chunks of maximal size. >>> [tuple(chunk) for chunk in iterslices('bacon', size=2)] @@ -153,7 +153,7 @@ def iterslices(iterable, *, size: int): return iter(lambda: list(next_slice()), []) -def walk_scandir(top, *, +def walk_scandir(top, /, *, verbose: bool = False, sortkey=operator.attrgetter('name')) -> typing.Iterator[os.DirEntry]: """Yield os.DirEntry objects for all files under top.""" @@ -188,8 +188,9 @@ def walk_scandir(top, *, stack.extend(reversed(dirs)) -def pipe_json_lines(file, documents=None, *, - delete_present: bool = True, autocompress: bool = True, +def pipe_json_lines(file, documents=None, /, *, + delete_present: bool = True, + autocompress: bool = True, newline: typing.Optional[str] = '\n', sort_keys: bool = True, compact: bool = True, @@ -230,7 +231,7 @@ def pipe_json_lines(file, documents=None, *, return pipe_json(lines, dump=False, **json_kwargs) -def pipe_json(documents, *, dump: bool, +def pipe_json(documents, /, *, dump: bool, sort_keys: bool = True, compact: bool = False, indent: typing.Optional[int] = None, @@ -265,7 +266,7 @@ def itercodec(docs): return itercodec(documents) -def pipe_lines(file, lines=None, *, newline: typing.Optional[str] = None, +def pipe_lines(file, lines=None, /, *, newline: typing.Optional[str] = None, delete_present: bool = False, autocompress: bool = True): open_func, result, hashobj = get_open_result(file, write=lines is not None, @@ -291,7 +292,7 @@ def iterlines(): return iterlines() -def write_wrapped(hashsum, f, lines, *, buflines: int = 1_000): +def write_wrapped(hashsum, f, lines, /, *, buflines: int = 1_000): write_line = functools.partial(print, file=f) buf = f.buffer total = 0 @@ -306,7 +307,7 @@ def write_wrapped(hashsum, f, lines, *, buflines: int = 1_000): return total -def write_lines(file, lines): +def write_lines(file, lines, /): r""" >>> with io.StringIO() as f: @@ -324,7 +325,7 @@ def write_lines(file, lines): return total -def path_from_filename(filename, *args, expanduser: bool = True): +def path_from_filename(filename, /, *args, expanduser: bool = True): if hasattr(filename, 'open'): assert not args result = filename @@ -336,7 +337,7 @@ def path_from_filename(filename, *args, expanduser: bool = True): return result -def get_open_result(file, *, write: bool = False, +def get_open_result(file, /, *, write: bool = False, delete_present: bool = False, autocompress: bool = False, newline: typing.Optional[str] = None, _encoding: str = 'utf-8'): @@ -389,7 +390,7 @@ def open_func(): return open_func, result, hashobj -def get_open_module(filepath, autocompress: bool = False): +def get_open_module(filepath, /, *, autocompress: bool = False): file = path_from_filename(filepath) suffix = file.suffix.lower() @@ -403,7 +404,7 @@ def get_open_module(filepath, autocompress: bool = False): return result -def sha256sum(file, *, raw: bool = False, autocompress: bool = True, +def sha256sum(file, /, *, raw: bool = False, autocompress: bool = True, hash_file_string: bool = False, file_string_encoding: str = ENCODING): """ @@ -426,12 +427,12 @@ def sha256sum(file, *, raw: bool = False, autocompress: bool = True, return hashobj if raw else hashobj.hexdigest() -def update_hashobj(hashobj, file, *, chunksize: int = 2**16): # 64 KiB +def update_hashobj(hashobj, file, /, *, chunksize: int = 2**16): # 64 KiB for chunk in iter(functools.partial(file.read, chunksize), b''): hashobj.update(chunk) -def run(cmd, *, capture_output: bool = False, +def run(cmd, /, *, capture_output: bool = False, unpack: bool = False, cwd=None, check: bool = False, encoding: str = ENCODING): log.info('subprocess.run(%r)', cmd) @@ -480,19 +481,19 @@ class Ordering(dict): _missing = float('inf') @classmethod - def fromlist(cls, keys, *, start_index: int = 0): + def fromlist(cls, keys, /, *, start_index: int = 0): return cls((k, i) for i, k in enumerate(uniqued(keys), start=start_index)) - def __missing__(self, key): + def __missing__(self, key, /): return self._missing - def _sortkey(self, key): + def _sortkey(self, key, /): return self[key], key - def sorted(self, keys): + def sorted(self, keys, /): return sorted(keys, key=self._sortkey) - def sorted_enumerate(self, keys, start: int = 0): + def sorted_enumerate(self, keys, /, *, start: int = 0): keyed = sorted((self[key], key) for key in keys) return ((i, key) for i, (_, key) in enumerate(keyed, start=start)) @@ -511,7 +512,7 @@ class ConfigParser(configparser.ConfigParser): _header = None @classmethod - def from_file(cls, filename, *, encoding=ENCODING, **kwargs): + def from_file(cls, filename, /, *, encoding=ENCODING, **kwargs): path = path_from_filename(filename) if cls._basename is not None and path.name != cls._basename: raise RuntimeError(f'unexpected filename {path!r}' @@ -522,18 +523,18 @@ def from_file(cls, filename, *, encoding=ENCODING, **kwargs): inst.read_file(f) return inst - def __init__(self, *, defaults=None, **kwargs): + def __init__(self, /, *, defaults=None, **kwargs): for k, v in self._init_defaults.items(): kwargs.setdefault(k, v) super().__init__(defaults=defaults, **kwargs) - def to_dict(self, *, sort_sections: bool = False, + def to_dict(self, /, *, sort_sections: bool = False, _default_section: str = configparser.DEFAULTSECT): items = sorted(self.items()) if sort_sections else self.items() return {name: dict(section) for name, section in items if name != _default_section} - def to_file(self, filename, *, encoding=ENCODING): + def to_file(self, filename, /, *, encoding=ENCODING): path = path_from_filename(filename) with path.open('wt', encoding=encoding, newline=self._newline) as f: if self._header is not None: diff --git a/treedb/backend/_basics.py b/treedb/backend/_basics.py index 7dfaa23..81e16a7 100644 --- a/treedb/backend/_basics.py +++ b/treedb/backend/_basics.py @@ -61,7 +61,7 @@ def print_versions(*, engine=ENGINE, file=None) -> None: engine=engine) -def set_engine(filename, *, +def set_engine(filename, /, *, resolve: bool = False, require: bool = False, title: typing.Optional[str] = None, @@ -145,12 +145,12 @@ def connect(*, bind=ENGINE, return conn -def scalar(statement, *args, bind=ENGINE, **kwargs): +def scalar(statement, /, *args, bind=ENGINE, **kwargs): with connect(bind=bind) as conn: return conn.scalar(statement, *args, **kwargs) -def iterrows(query, *, mappings=False, bind=ENGINE): +def iterrows(query, /, *, mappings=False, bind=ENGINE): with connect(bind=bind) as conn: result = conn.execute(query) @@ -160,7 +160,7 @@ def iterrows(query, *, mappings=False, bind=ENGINE): yield from result -def expression_compile(expression, *, literal_binds=True): +def expression_compile(expression, /, *, literal_binds=True): """Return literal compiled expression.""" return expression.compile(compile_kwargs={'literal_binds': literal_binds}) @@ -176,6 +176,6 @@ def json_object(*, sort_keys_: bool, return sa.type_coerce(obj, sa.JSON) if load_json_ else obj -def json_datetime(date): +def json_datetime(date, /): date = sa.func.replace(date, ' ', 'T') return sa.func.replace(date, '.000000', '') diff --git a/treedb/backend/export.py b/treedb/backend/export.py index 2d1da87..52ee089 100644 --- a/treedb/backend/export.py +++ b/treedb/backend/export.py @@ -52,8 +52,8 @@ def print_dataset(*, ignore_dirty: bool = False, also_print=True, print_file=file) -def print_schema(metadata=_globals.REGISTRY.metadata, - *, file=None, +def print_schema(metadata=_globals.REGISTRY.metadata, /, *, + file=None, engine=_globals.ENGINE): """Print the SQL from metadata.create_all() without executing.""" def print_sql(sql, *_, **__): @@ -65,7 +65,7 @@ def print_sql(sql, *_, **__): metadata.create_all(mock_engine, checkfirst=False) -def print_query_sql(query=None, *, literal_binds: bool = True, +def print_query_sql(query=None, /, *, literal_binds: bool = True, pretty: bool = True, file=None, flush: bool = True): """Print the literal SQL for the given query.""" @@ -73,7 +73,7 @@ def print_query_sql(query=None, *, literal_binds: bool = True, print(sql, file=file, flush=flush) -def get_query_sql(query=None, *, literal_binds: bool = True, +def get_query_sql(query=None, /, *, literal_binds: bool = True, pretty: bool = False): """Return the literal SQL for the given query.""" if query is None: @@ -89,7 +89,7 @@ def get_query_sql(query=None, *, literal_binds: bool = True, return result -def backup(filename=None, *, as_new_engine: bool = False, +def backup(filename=None, /, *, as_new_engine: bool = False, pages: int = 0, engine=_globals.ENGINE): """Write the database into another .sqlite3 file and return its engine.""" @@ -129,8 +129,8 @@ def progress(status, remaining, total): return result -def dump_sql(filename=None, - *, progress_after: int = 100_000, +def dump_sql(filename=None, /, *, + progress_after: int = 100_000, encoding: str = _tools.ENCODING, engine=_globals.ENGINE): """Dump the engine database into a plain-text SQL file.""" @@ -160,7 +160,7 @@ def dump_sql(filename=None, return path -def csv_zipfile(filename=None, *, exclude_raw: bool = False, +def csv_zipfile(filename=None, /, *, exclude_raw: bool = False, metadata=_globals.REGISTRY.metadata, dialect=csv23.DIALECT, encoding: str = csv23.ENCODING, engine=_globals.ENGINE): @@ -206,7 +206,7 @@ def csv_zipfile(filename=None, *, exclude_raw: bool = False, return _tools.path_from_filename(filename) -def print_rows(query=None, *, file=None, +def print_rows(query=None, /, *, file=None, pretty: bool = False, format_: typing.Optional[str] = None, verbose: bool = False, @@ -241,7 +241,7 @@ def print_rows(query=None, *, file=None, class PrettyPrinter(pprint.PrettyPrinter): - def __init__(self, stream, *, + def __init__(self, stream, /, *, sort_dicts: bool = False, **kwargs) -> None: if sys.version_info < (3, 8): @@ -255,8 +255,8 @@ def __init__(self, stream, *, super().__init__(stream=stream, **kwargs) -def write_csv(query=None, filename=None, - *, verbose: bool = False, +def write_csv(query=None, /, filename=None, *, + verbose: bool = False, dialect=csv23.DIALECT, encoding: str = csv23.ENCODING, bind=_globals.ENGINE): """Write get__example_query() query (or given query) to CSV, return filename.""" @@ -288,7 +288,7 @@ def write_csv(query=None, filename=None, autocompress=True) -def hash_csv(query=None, *, hash_name: str = _globals.DEFAULT_HASH, +def hash_csv(query=None, /, *, hash_name: str = _globals.DEFAULT_HASH, dialect=csv23.DIALECT, encoding: str = csv23.ENCODING, raw: bool = False, bind=_globals.ENGINE): @@ -305,7 +305,7 @@ def hash_csv(query=None, *, hash_name: str = _globals.DEFAULT_HASH, dialect=dialect, encoding=encoding) -def hash_rows(rows, *, hash_name: str = _globals.DEFAULT_HASH, +def hash_rows(rows, /, *, hash_name: str = _globals.DEFAULT_HASH, header=None, dialect=csv23.DIALECT, encoding=csv23.ENCODING, raw: bool = False): diff --git a/treedb/backend/load.py b/treedb/backend/load.py index 140704c..b7f25ac 100644 --- a/treedb/backend/load.py +++ b/treedb/backend/load.py @@ -24,7 +24,7 @@ log = logging.getLogger(__name__) -def get_root(repo_root, *, default, +def get_root(repo_root, /, *, default, treepath=_languoids.TREE_IN_ROOT): if repo_root is not None: root = _languoids.set_root(repo_root, treepath=treepath) @@ -38,7 +38,7 @@ def get_root(repo_root, *, default, return root -def get_from_raw(from_raw, *, exclude_raw: bool): +def get_from_raw(from_raw, /, *, exclude_raw: bool): if exclude_raw and from_raw: # pragma: no cover log.error('incompatible exclude_raw=%r and from_raw=%r', exclude_raw, from_raw) raise ValueError('exclude_raw and from_raw cannot both be True') @@ -47,13 +47,13 @@ def get_from_raw(from_raw, *, exclude_raw: bool): return from_raw -def get_engine(filename_or_engine, *, require: bool): +def get_engine(filename_or_engine, /, *, require: bool): if hasattr(filename_or_engine, 'execute'): return filename_or_engine return _backend.set_engine(filename_or_engine, require=require) -def get_dataset(engine, *, exclude_raw: bool, strict: bool): +def get_dataset(engine, /, *, exclude_raw: bool, strict: bool): dataset = None if engine.file is None: @@ -71,8 +71,8 @@ def get_dataset(engine, *, exclude_raw: bool, strict: bool): return dataset -def main(filename=_globals.ENGINE, repo_root=None, - *, treepath=_languoids.TREE_IN_ROOT, +def main(filename=_globals.ENGINE, repo_root=None, /, *, + treepath=_languoids.TREE_IN_ROOT, metadata=_globals.REGISTRY.metadata, require: bool = False, rebuild: bool = False, @@ -130,7 +130,7 @@ def main(filename=_globals.ENGINE, repo_root=None, return engine -def create_tables(metadata, *, conn, +def create_tables(metadata, /, *, conn, exclude_raw: bool, exclude_views: bool): # import here to register models for create_all() log.debug('import module %s.models', __package__) @@ -164,7 +164,7 @@ def create_tables(metadata, *, conn, metadata.create_all(bind=conn) -def load(metadata, *, conn, root, +def load(metadata, /, *, conn, root, from_raw: bool, exclude_raw: bool): log.info('record git commit in %r', root) # pre-create dataset to added as final item marking completeness @@ -208,7 +208,7 @@ def load(metadata, *, conn, root, return dataset -def import_configs(conn, *, root): +def import_configs(conn, /, *, root): insert_config = functools.partial(conn.execute, sa.insert(_models.Config)) for filename, cfg in _config.iterconfigs(root): get_line = _tools.next_count(start=1) @@ -227,7 +227,7 @@ def import_configs(conn, *, root): return conn.execute(select_version).scalar_one_or_none() -def make_dataset(root, *, exclude_raw: bool): +def make_dataset(root, /, *, exclude_raw: bool): try: dataset = {'title': 'Glottolog treedb', 'git_commit': _glottolog.git_rev_parse(root), @@ -243,12 +243,12 @@ def make_dataset(root, *, exclude_raw: bool): return dataset -def write_dataset(conn, *, dataset): +def write_dataset(conn, /, *, dataset): log.debug('dataset: %r', dataset) conn.execute(sa.insert(_models.Dataset), dataset) -def write_producer(conn, *, name: str): +def write_producer(conn, /, *, name: str): from .. import __version__ params = {'name': name, 'version': __version__} @@ -256,7 +256,7 @@ def write_producer(conn, *, name: str): conn.execute(sa.insert(_models.Producer), params) -def import_raw(conn, *, root): +def import_raw(conn, /, *, root): log.debug('import target module %s.raw.import_models', __package__) from ..raw import import_models @@ -266,7 +266,7 @@ def import_raw(conn, *, root): import_models.main(root, conn=conn) -def import_languoids(conn, *, root, source: str): +def import_languoids(conn, /, *, root, source: str): log.debug('import source module %s.languoids', __package__) from .. import export diff --git a/treedb/backend/models.py b/treedb/backend/models.py index 7224431..ec7cd9d 100644 --- a/treedb/backend/models.py +++ b/treedb/backend/models.py @@ -37,7 +37,7 @@ class Dataset: exclude_raw = sa.Column(sa.Boolean(create_constraint=True), nullable=False) @classmethod - def get_dataset(cls, *, bind, strict, fallback=None): + def get_dataset(cls, /, *, bind, strict, fallback=None): table = cls.__tablename__ log.debug('read %r from %r', table, bind) @@ -64,7 +64,7 @@ def get_dataset(cls, *, bind, strict, fallback=None): return result @classmethod - def log_dataset(cls, params, *, + def log_dataset(cls, params, /, *, ignore_dirty: bool = False, also_print: bool = False, print_file=None): name = cls.__tablename__ @@ -108,12 +108,13 @@ class Producer: nullable=False) @classmethod - def get_producer(cls, *, bind): + def get_producer(cls, /, *, bind): result, = _backend.iterrows(sa.select(cls), mappings=True, bind=bind) return result @classmethod - def log_producer(cls, params, *, also_print=False, print_file=None): + def log_producer(cls, params, /, *, + also_print: bool = False, print_file=None): name = cls.__tablename__ log.info('%s.name: %s', name, params['name']) log.info('%s.version: %s', name, params['version']) @@ -145,7 +146,7 @@ class Config: {'info': {'without_rowid': True}}) @classmethod - def load(cls, filename: str, *, bind, + def load(cls, filename: str, /, *, bind, _groupby_section=_tools.groupby_itemgetter(0)): select_values = (sa.select(Config.section, Config.option, Config.value) .filter_by(filename=filename) diff --git a/treedb/backend/pandas.py b/treedb/backend/pandas.py index 01e08c8..8343d42 100644 --- a/treedb/backend/pandas.py +++ b/treedb/backend/pandas.py @@ -33,7 +33,7 @@ def _import_pandas(): log.info('pandas version: %s', PANDAS.__version__) -def pd_read_sql(sql=None, *args, con=_globals.ENGINE, **kwargs): +def pd_read_sql(sql=None, /, *args, con=_globals.ENGINE, **kwargs): _import_pandas() if PANDAS is None: @@ -48,8 +48,8 @@ def pd_read_sql(sql=None, *args, con=_globals.ENGINE, **kwargs): return PANDAS.read_sql_query(sql, *args, con=conn, **kwargs) -def pd_read_json_lines(query, - *, buflines: int = JSON_BUFLINES, +def pd_read_json_lines(query, /, *, + buflines: int = JSON_BUFLINES, bind=_globals.ENGINE, **kwargs): _import_pandas() @@ -63,8 +63,8 @@ def pd_read_json_lines(query, return _pd_read_json_lines(json_lines, **kwargs) -def _pd_read_json_lines(json_lines: typing.Iterable[str], - *, buflines: int = JSON_BUFLINES, +def _pd_read_json_lines(json_lines: typing.Iterable[str], /, *, + buflines: int = JSON_BUFLINES, concat_ignore_index: bool = False, **kwargs): _import_pandas() diff --git a/treedb/backend/sqlite_master.py b/treedb/backend/sqlite_master.py index 6e945d1..663f2a7 100644 --- a/treedb/backend/sqlite_master.py +++ b/treedb/backend/sqlite_master.py @@ -29,7 +29,7 @@ sa.column('sql', sa.Text)) -def print_table_sql(model_or_table, *, include_nrows: bool = True, +def print_table_sql(model_or_table, /, *, include_nrows: bool = True, file=None, flush: bool = True, bind=_globals.ENGINE): """Print CREATE TABLE for the given table and its number of rows.""" @@ -48,7 +48,7 @@ def print_table_sql(model_or_table, *, include_nrows: bool = True, print(nrows, file=file, flush=flush) -def _get_table_name(model_or_table): +def _get_table_name(model_or_table, /): if hasattr(model_or_table, '__tablename__'): return model_or_table.__tablename__ elif hasattr(model_or_table, 'name'): @@ -56,7 +56,7 @@ def _get_table_name(model_or_table): return model_or_table -def select_table_sql(model_or_table): +def select_table_sql(model_or_table, /): """Select CREATE_TABLE of the given table from sqlite_master.""" select = (sa.select(sqlite_master.c.sql) .select_from(sqlite_master) @@ -69,7 +69,7 @@ def select_table_sql(model_or_table): return select -def select_table_nrows(model_or_table, *, label: str = 'n_rows'): +def select_table_nrows(model_or_table, /, *, label: str = 'n_rows'): """Select the number of rows for the given table.""" table_name = _get_table_name(model_or_table) return (sa.select(sa.func.count().label(label)) diff --git a/treedb/backend/views.py b/treedb/backend/views.py index 0ca3cf9..8cca218 100644 --- a/treedb/backend/views.py +++ b/treedb/backend/views.py @@ -35,14 +35,14 @@ def decorator(func): return decorator -def create_all_views(*, clear=False): +def create_all_views(*, clear: bool = False): log.debug('run create_view() for %d views in REGISTERED', len(REGISTERED)) for name, func in REGISTERED.items(): table = view(name, selectable=func(), clear=clear) setattr(TABLES, name, table) -def view(name, selectable, *, clear=False): +def view(name, selectable, *, clear: bool = False): """Register a CREATE and DROP VIEW DDL for the given selectable.""" log.debug('view(%r, clear=%r)', name, clear) @@ -56,7 +56,7 @@ def view(name, selectable, *, clear=False): return make_table(selectable, name=name) -def make_table(selectable, *, name='view_table'): +def make_table(selectable, /, *, name: str = 'view_table'): table = sa.table(name) for c in selectable.alias().c: _, col = c._make_proxy(table) diff --git a/treedb/checks.py b/treedb/checks.py index d72278f..40c7c2c 100644 --- a/treedb/checks.py +++ b/treedb/checks.py @@ -21,7 +21,7 @@ log = logging.getLogger(__name__) -def check(func=None, *, bind=_globals.ENGINE): +def check(func=None, /, *, bind=_globals.ENGINE): """Run consistency/sanity checks on database.""" if func is not None: try: @@ -58,11 +58,11 @@ class Check(object): detail = True - def __init__(self, session, **kwargs): + def __init__(self, session, /, **kwargs): self.session = session self.query = self.invalid_query(**kwargs) - def invalid_query(self, **kwargs): # pragma: no cover + def invalid_query(self, /, **kwargs): # pragma: no cover raise NotImplementedError def validate(self): @@ -94,7 +94,7 @@ def __str__(self): return f'{self.__class__.__name__}: {msg}' @staticmethod - def show_detail(invalid, invalid_count, number=25): # pragma: no cover + def show_detail(invalid, invalid_count, /, *, number=25): # pragma: no cover ids = (i.id for i in itertools.islice(invalid, number)) cont = ', ...' if number < invalid_count else '' print(f" {', '.join(ids)}{cont}") @@ -273,7 +273,7 @@ def no_empty_files(*, exclude_raw: bool): .where(~sa.exists().where(Value.file_id == File.id))) -def compare_languoids(left_source: str = 'files', right_source: str = 'raw', +def compare_languoids(left_source: str = 'files', right_source: str = 'raw', /, *, order_by: str = _globals.LANGUOID_ORDER): # pragma: no cover from . import export diff --git a/treedb/config.py b/treedb/config.py index 146cd5f..a4af91a 100644 --- a/treedb/config.py +++ b/treedb/config.py @@ -16,17 +16,17 @@ log = logging.getLogger(__name__) -def get_config_path(root=_globals.ROOT, - *, configpath=CONFIG_IN_ROOT): +def get_config_path(root=_globals.ROOT, /, *, + configpath=CONFIG_IN_ROOT): return _languoids.get_repo_root(root) / configpath -def iterconfigs(root=_globals.ROOT, *, glob='*.ini'): +def iterconfigs(root=_globals.ROOT, /, *, glob='*.ini'): for path in get_config_path().glob(glob): yield path.name, load_config(path) -def load_config(filepath, *, sort_sections: bool = False +def load_config(filepath, /, *, sort_sections: bool = False ) -> typing.Dict[str, typing.Dict[str, str]]: log.debug('open config file from path: %r', filepath) cfg = _tools.ConfigParser.from_file(filepath) diff --git a/treedb/export.py b/treedb/export.py index 27f442a..8b40eaf 100644 --- a/treedb/export.py +++ b/treedb/export.py @@ -57,8 +57,8 @@ def print_languoid_stats(*, file=None, warnings.warn(f'{term} = {parts_sum:,d}') -def iterlanguoids(source: str = 'files', - *, limit: typing.Optional[int] = None, +def iterlanguoids(source: str = 'files', /, *, + limit: typing.Optional[int] = None, offset: typing.Optional[int] = 0, order_by: str = _globals.LANGUOID_ORDER, progress_after: int = _tools.PROGRESS_AFTER, @@ -100,8 +100,8 @@ def iterlanguoids(source: str = 'files', raise ValueError(f'unknown source: {source!r}') -def checksum(source: str = 'tables', - *, limit: typing.Optional[int] = None, +def checksum(source: str = 'tables', *, + limit: typing.Optional[int] = None, offset: typing.Optional[int] = 0, order_by: str = _globals.LANGUOID_ORDER, hash_name: str = _globals.DEFAULT_HASH, @@ -129,7 +129,7 @@ def checksum(source: str = 'tables', return result -def write_json_lines(file=None, *, suffix: str = '.jsonl', +def write_json_lines(file=None, /, *, suffix: str = '.jsonl', delete_present: bool = True, autocompress: bool = True, source: str = 'tables', @@ -273,7 +273,7 @@ def fetch_languoids(*, limit: typing.Optional[int] = None, log.info('%s languoids total', f'{n:_d}') -def write_files(root=_globals.ROOT, *, replace: bool = False, +def write_files(root=_globals.ROOT, /, *, replace: bool = False, dry_run: bool = False, require_nwritten: typing.Optional[int] = None, source: str = 'tables', diff --git a/treedb/glottolog.py b/treedb/glottolog.py index 4dfaa86..683825e 100644 --- a/treedb/glottolog.py +++ b/treedb/glottolog.py @@ -20,19 +20,19 @@ log = logging.getLogger(__name__) -def glottolog_version(root=_globals.ROOT) -> argparse.Namespace: +def glottolog_version(*, root=_globals.ROOT) -> argparse.Namespace: return GlottologVersion.from_root(root) class GlottologVersion(argparse.Namespace): @classmethod - def from_root(cls, root) -> GlottologVersion: + def from_root(cls, root, /) -> GlottologVersion: return cls.from_commit_describe(git_rev_parse(root), git_describe(root)) @classmethod - def from_commit_describe(cls, commit: str, describe: str + def from_commit_describe(cls, commit: str, describe: str, /, ) -> GlottologVersion: return cls(commit=commit, describe=describe) @@ -40,7 +40,7 @@ def __str__(self) -> str: return f'Glottolog {self.describe} ({self.commit})' -def checkout_or_clone(tag_or_branch: str, *, target=None): +def checkout_or_clone(tag_or_branch: str, /, *, target=None): if target is None: target = _languoids.get_repo_root() @@ -55,7 +55,7 @@ def checkout_or_clone(tag_or_branch: str, *, target=None): return clone, checkout -def git_clone(tag_or_branch: str, *, target, +def git_clone(tag_or_branch: str, /, *, target, depth: int = 1): log.info('clone Glottolog master repo at %r into %r', tag_or_branch, target) cmd = ['git', 'clone', @@ -67,7 +67,7 @@ def git_clone(tag_or_branch: str, *, target, return _tools.run(cmd, check=True) -def git_checkout(tag_or_branch: str, *, target, +def git_checkout(tag_or_branch: str, /, *, target, set_branch: str = __package__): log.info('checkout %r and (re)set branch %r', tag_or_branch, set_branch) cmd = ['git', 'checkout'] @@ -77,8 +77,8 @@ def git_checkout(tag_or_branch: str, *, target, return _tools.run(cmd, cwd=target, check=True) -def git_rev_parse(repo_root, revision: str = 'HEAD', - *, verify: bool = True) -> str: +def git_rev_parse(repo_root, /, *, revision: str = 'HEAD', + verify: bool = True) -> str: log.info('get %r git_commit from %r', revision, repo_root) cmd = ['git', 'rev-parse'] if verify: @@ -90,7 +90,7 @@ def git_rev_parse(repo_root, revision: str = 'HEAD', return commit -def git_describe(repo_root) -> str: +def git_describe(repo_root, /) -> str: log.info('get git_describe from %r', repo_root) cmd = ['git', 'describe', '--tags', '--always'] describe = _tools.run(cmd, cwd=repo_root, check=True, @@ -99,14 +99,14 @@ def git_describe(repo_root) -> str: return describe -def git_status(repo_root) -> str: +def git_status(repo_root, /) -> str: log.debug('get status from %r', repo_root) cmd = ['git', 'status', '--porcelain'] return _tools.run(cmd, cwd=repo_root, check=True, capture_output=True, unpack=True) -def git_status_is_clean(repo_root) -> bool: +def git_status_is_clean(repo_root, /) -> bool: """Return if there are neither changes in the index nor untracked files.""" log.info('get clean from %r', repo_root) status = git_status(repo_root) diff --git a/treedb/import_models.py b/treedb/import_models.py index ea1a724..a9fb764 100644 --- a/treedb/import_models.py +++ b/treedb/import_models.py @@ -37,8 +37,8 @@ class ModelMap(dict): def key_to_params(key): return {'name': key} - def __init__(self, items=(), - *, conn, + def __init__(self, items=(), /, *, + conn, model=None, key_to_params=None, log_insert=True): super().__init__(items) @@ -51,7 +51,7 @@ def __init__(self, items=(), self.conn = conn self.insert = functools.partial(conn.execute, sa.insert(self.model)) - def __missing__(self, key): + def __missing__(self, key, /): if self.log_insert: log.debug('insert new %s: %r', self.model.__tablename__, key) params = self.key_to_params(key) @@ -62,7 +62,7 @@ def __missing__(self, key): return pk -def main(languoids, *, conn): +def main(languoids, /, *, conn): bibfile_ids = ModelMap(conn=conn, model=Bibfile) @@ -113,7 +113,7 @@ def params_to_key(params): insert_pseudofamilies(conn) -def insert_languoid_levels(conn, *, config_file='languoid_levels.ini'): +def insert_languoid_levels(conn, /, *, config_file='languoid_levels.ini'): log.info('insert languoid levels from: %r', config_file) levels = Config.load(config_file, bind=conn) levels = dict(sorted(levels.items(), key=lambda x: int(x[1]['ordinal']))) @@ -132,7 +132,7 @@ def insert_languoid_levels(conn, *, config_file='languoid_levels.ini'): conn.execute(sa.insert(LanguoidLevel), params) -def insert_pseudofamilies(conn, *, config_file='language_types.ini'): +def insert_pseudofamilies(conn, /, *, config_file='language_types.ini'): log.info('insert pseudofamilies from: %r', config_file) languagetypes = Config.load(config_file, bind=conn) pseudofamilies = {section: l for section, l in languagetypes.items() @@ -168,7 +168,7 @@ def insert_pseudofamilies(conn, *, config_file='language_types.ini'): f' expected bookkeeping: {BOOKKEEPING!r}') -def insert_macroareas(conn, *, config_file='macroareas.ini'): +def insert_macroareas(conn, /, *, config_file='macroareas.ini'): log.info('insert macroareas from: %r', config_file) macroareas = Config.load(config_file, bind=conn) @@ -180,7 +180,7 @@ def insert_macroareas(conn, *, config_file='macroareas.ini'): conn.execute(sa.insert(Macroarea), params) -def insert_endangermentstatus(conn, *, bibitem_ids, +def insert_endangermentstatus(conn, /, *, bibitem_ids, config_file='aes_status.ini'): log.info('insert endangermentstatus from %r:', config_file) status = Config.load(config_file, bind=conn) @@ -199,7 +199,7 @@ def insert_endangermentstatus(conn, *, bibitem_ids, conn.execute(sa.insert(EndangermentStatus), params) -def insert_languoids(conn, *, languoids, bibitem_ids, es_ids): +def insert_languoids(conn, /, *, languoids, bibitem_ids, es_ids): log.info('insert languoids') def unseen_countries(countries, _seen={}): @@ -224,7 +224,7 @@ def unseen_countries(countries, _seen={}): insert_languoid(l, **kwargs) -def insert_languoid(languoid, *, conn, +def insert_languoid(languoid, /, *, conn, insert_lang, unseen_countries, sourceprovider_ids, diff --git a/treedb/languoids/__init__.py b/treedb/languoids/__init__.py index 2e5bce1..99be686 100644 --- a/treedb/languoids/__init__.py +++ b/treedb/languoids/__init__.py @@ -13,8 +13,8 @@ 'iterrecords'] -def iterrecords(root=_globals.ROOT, - *, progress_after: int = _tools.PROGRESS_AFTER +def iterrecords(*, root=_globals.ROOT, + progress_after: int = _tools.PROGRESS_AFTER ) -> typing.Iterable[_globals.RecordItem]: for path_tuple, _, cfg in iterfiles(root, progress_after=progress_after): yield path_tuple, cfg diff --git a/treedb/languoids/_root.py b/treedb/languoids/_root.py index 6193ec9..03cf045 100644 --- a/treedb/languoids/_root.py +++ b/treedb/languoids/_root.py @@ -13,7 +13,7 @@ log = logging.getLogger(__name__) -def set_root(repo_root, *, resolve=False, +def set_root(repo_root, /, *, resolve: bool = False, treepath=TREE_IN_ROOT): """Set and return default root for glottolog lanugoid directory tree.""" log.info('set_root: %r', repo_root) @@ -29,8 +29,8 @@ def set_root(repo_root, *, resolve=False, return ROOT -def get_repo_root(root=_globals.ROOT, - *, treepath=TREE_IN_ROOT): +def get_repo_root(root=_globals.ROOT, /, *, + treepath=TREE_IN_ROOT): assert root.parts[-len(treepath.parts):] == treepath.parts repo_root = _tools.path_from_filename(root) for _ in treepath.parts: diff --git a/treedb/languoids/fields.py b/treedb/languoids/fields.py index 541d931..5c91def 100644 --- a/treedb/languoids/fields.py +++ b/treedb/languoids/fields.py @@ -100,17 +100,17 @@ log = logging.getLogger(__name__) -def is_known(section, option): +def is_known(section, option, /): """Retun True if the section option is known or in an ALL_OPTIONS section.""" return (section, ALL_OPTIONS) in FIELDS or (section, option) in FIELDS -def is_all_options(section, option): +def is_all_options(section, option, /): """Retun True if the section option is in an ALL_OPTIONS section.""" return (section, ALL_OPTIONS) in FIELDS -def is_lines(section, option, *, unknown_as_scalar=True): +def is_lines(section, option, /, *, unknown_as_scalar: bool = True): """Return True if the section option is treated as list of lines.""" result = FIELDS.get((section, ALL_OPTIONS)) @@ -133,13 +133,13 @@ def is_lines(section, option, *, unknown_as_scalar=True): sorted_sections = SECTION_ORDER.sorted -def sorted_options(section, options): +def sorted_options(section, options, /): """Return the given section options as sorted list in canonical order.""" fields = FIELD_ORDER.sorted((section, o) for o in options) return [o for _, o in fields] -def parse_lines(value): +def parse_lines(value, /): r""" >>> parse_lines(None) @@ -153,7 +153,7 @@ def parse_lines(value): return value.strip().splitlines() -def format_lines(value): +def format_lines(value, /): r""" >>> format_lines(['spam', 'eggs']) @@ -172,7 +172,7 @@ def format_lines(value): RawRecordItem = typing.Tuple[typing.Optional[_globals.PathType], RawRecordType] -def join_lines_inplace(record_item: _globals.RecordItem) -> RawRecordItem: +def join_lines_inplace(record_item: _globals.RecordItem, /) -> RawRecordItem: path, record = record_item for name, section in record.items(): for option in section: diff --git a/treedb/languoids/files.py b/treedb/languoids/files.py index 65804c8..2be7a9a 100644 --- a/treedb/languoids/files.py +++ b/treedb/languoids/files.py @@ -30,8 +30,8 @@ class ConfigParser(_tools.ConfigParser): _header = '# -*- coding: {encoding} -*-\n' - def update_config(self, raw_record: _fields.RawRecordType, - *, replace: bool = False, + def update_config(self, raw_record: _fields.RawRecordType, /, *, + replace: bool = False, quiet: bool = False, is_lines=_fields.is_lines, core_sections=_fields.CORE_SECTIONS, @@ -128,15 +128,15 @@ class FileInfo(typing.NamedTuple): config: ConfigParser @classmethod - def from_dentry(cls, dentry: os.DirEntry, - *, path_slice: slice = slice(None)): + def from_dentry(cls, dentry: os.DirEntry, /, *, + path_slice: slice = slice(None)): path = _tools.path_from_filename(dentry) config = ConfigParser.from_file(path) return cls(path.parts[path_slice], dentry, config) -def iterfiles(root=_globals.ROOT, - *, progress_after: int = _tools.PROGRESS_AFTER +def iterfiles(root=_globals.ROOT, /, *, + progress_after: int = _tools.PROGRESS_AFTER ) -> typing.Iterator[FileInfo]: """Yield triples of ((, ...), , ).""" root = _tools.path_from_filename(root).resolve() @@ -156,15 +156,15 @@ def iterfiles(root=_globals.ROOT, log.info(f'%s {BASENAME} files total', f'{n:_d}') -def roundtrip(root=_globals.ROOT, - *, progress_after: int = _tools.PROGRESS_AFTER) -> None: +def roundtrip(root=_globals.ROOT, /, *, + progress_after: int = _tools.PROGRESS_AFTER) -> None: """Load/save all config files (drops leading/trailing whitespace).""" log.info(f'start roundtripping {BASENAME} files in %r', root) for path_tuple, dentry, cfg in iterfiles(root, progress_after=progress_after): cfg.to_file(dentry.path) -def write_files(records: typing.Iterable[_globals.RecordItem], +def write_files(records: typing.Iterable[_globals.RecordItem], /, root=_globals.ROOT, *, replace: bool = False, dry_run: bool = False, quiet: typing.Optional[bool] = None, require_nwritten: typing.Optional[int] = None, diff --git a/treedb/languoids/records.py b/treedb/languoids/records.py index 6b53275..33fcfe9 100644 --- a/treedb/languoids/records.py +++ b/treedb/languoids/records.py @@ -47,8 +47,8 @@ def pipe(items, /, *, dump: bool, return codec(items, convert_lines=convert_lines) -def _parse(records: typing.Iterable[_globals.RecordItem], - /, *, convert_lines: bool) -> typing.Iterator[_globals.LanguoidItem]: +def _parse(records: typing.Iterable[_globals.RecordItem], /, *, + convert_lines: bool) -> typing.Iterator[_globals.LanguoidItem]: r"""Yield languoid items from given record ítems (from raw). >>> dict(pipe({('abin1243',): @@ -134,8 +134,8 @@ def _parse(records: typing.Iterable[_globals.RecordItem], log.info('%s languoids extracted from records', f'{n:_d}') -def _dump(languoids: typing.Iterable[_globals.LanguoidItem], - /, *, convert_lines: bool) -> typing.Iterator[_globals.RecordItem]: +def _dump(languoids: typing.Iterable[_globals.LanguoidItem], /, *, + convert_lines: bool) -> typing.Iterator[_globals.RecordItem]: r""" >>> dict(pipe({('abin1243',): { @@ -226,8 +226,8 @@ def _dump(languoids: typing.Iterable[_globals.LanguoidItem], yield path, record -def make_languoid(path_tuple: _globals.PathType, cfg: _globals.RecordType, - /, *, convert_lines: bool) -> _globals.LanguoidType: +def make_languoid(path_tuple: _globals.PathType, cfg: _globals.RecordType, /, *, + convert_lines: bool) -> _globals.LanguoidType: _make_lines = _fields.parse_lines if convert_lines else make_lines_raw core = cfg[CORE] @@ -317,8 +317,8 @@ def make_languoid(path_tuple: _globals.PathType, cfg: _globals.RecordType, return languoid -def make_record(languoid: _globals.LanguoidType, - /, *, convert_lines: bool, +def make_record(languoid: _globals.LanguoidType, /, *, + convert_lines: bool, is_lines=_fields.is_lines) -> _globals.RecordType: core = {'name': languoid['name'], 'hid': languoid['hid'], diff --git a/treedb/logging_.py b/treedb/logging_.py index 53b5d8e..e42fc2f 100644 --- a/treedb/logging_.py +++ b/treedb/logging_.py @@ -16,11 +16,11 @@ log = logging.getLogger(__name__) -def configure_logging_from_file(path, *, +def configure_logging_from_file(path, /, *, level=None, log_sql=None, - capture_warnings=True, - reset=True): + capture_warnings: bool = True, + reset: bool = True): if reset: reset_logging() @@ -59,7 +59,7 @@ def reset_logging(): h.close() -def log_version(*, also_print=False, print_file=None): +def log_version(*, also_print: bool = False, print_file=None): import treedb log.info('%s version: %s', __package__, treedb.__version__) @@ -69,7 +69,7 @@ def log_version(*, also_print=False, print_file=None): file=print_file) -def set_capture_warnings(value=True): +def set_capture_warnings(value: bool = True, /): log.debug('set logging.captureWarnings(%r)', value) logging.captureWarnings(value) diff --git a/treedb/models.py b/treedb/models.py index 1128023..22b9334 100644 --- a/treedb/models.py +++ b/treedb/models.py @@ -166,7 +166,7 @@ def __repr__(self): back_populates='languoid') @classmethod - def _aliased_child_parent(cls, *, + def _aliased_child_parent(cls, /, *, child_root: bool = False, parent_root: bool = False): if child_root and parent_root: # pragma: no cover @@ -177,7 +177,7 @@ def _aliased_child_parent(cls, *, return Child, Parent @classmethod - def _tree(cls, *, from_parent: bool = False, + def _tree(cls, /, *, from_parent: bool = False, innerjoin=False, child_root=None, parent_root=None, node_level=None, with_steps: bool = False, @@ -267,7 +267,7 @@ def _tree(cls, *, from_parent: bool = False, return tree @classmethod - def tree(cls, *, include_self: bool = False, + def tree(cls, /, *, include_self: bool = False, with_steps: bool = False, with_terminal: bool = False): return cls._tree(from_parent=False, @@ -275,7 +275,7 @@ def tree(cls, *, include_self: bool = False, with_steps=with_steps, with_terminal=with_terminal) @classmethod - def _path_part(cls, label: str = 'path_part', + def _path_part(cls, /, *, label: str = 'path_part', include_self: bool = True, bottomup: bool = False, _tree=None): @@ -294,7 +294,7 @@ def _path_part(cls, label: str = 'path_part', return select_path_part @classmethod - def path(cls, *, label: str = 'path', + def path(cls, /, *, label: str = 'path', delimiter: str = _globals.FILE_PATH_SEP, include_self: bool = True, bottomup: bool = False, @@ -305,7 +305,7 @@ def path(cls, *, label: str = 'path', return sa.select(path).label(label) @classmethod - def node_relative(cls, *, from_parent: bool = False, + def node_relative(cls, /, *, from_parent: bool = False, innerjoin=False, child_root=None, parent_root=None, node_level=None, with_steps: bool = False, @@ -337,7 +337,7 @@ def node_relative(cls, *, from_parent: bool = False, return Node, Relative, tree, node_relative @classmethod - def child_ancestor(cls, *, innerjoin=False, + def child_ancestor(cls, /, *, innerjoin=False, child_level=None): Child, Parent, _, child_parent = cls.node_relative(from_parent=False, # noqa: N806 innerjoin=innerjoin, @@ -345,7 +345,7 @@ def child_ancestor(cls, *, innerjoin=False, return Child, Parent, _, child_parent @classmethod - def parent_descendant(cls, *, innerjoin=False, + def parent_descendant(cls, /, *, innerjoin=False, parent_root=None, parent_level=None): Parent, Child, _, parent_child = cls.node_relative(from_parent=True, # noqa: N806 innerjoin=innerjoin, @@ -354,7 +354,7 @@ def parent_descendant(cls, *, innerjoin=False, return Parent, Child, parent_child @classmethod - def path_family_language(cls, *, path_label: str = 'path', + def path_family_language(cls, /, *, path_label: str = 'path', path_delimiter: str = _globals.FILE_PATH_SEP, include_self: bool = True, bottomup: bool = False, @@ -501,12 +501,12 @@ def __repr__(self): back_populates='countries') @classmethod - def printf(cls, *, minimal: bool = True, label: str = 'printf'): + def printf(cls, /, *, minimal: bool = True, label: str = 'printf'): return (sa.func.printf('%s (%s)', cls.name, cls.id) if not minimal else cls.id).label(label) @classmethod - def jsonf(cls, *, sort_keys: bool, label: str = 'jsonf'): + def jsonf(cls, /, *, sort_keys: bool, label: str = 'jsonf'): return json_object(id=cls.id, name=cls.name, sort_keys_=sort_keys, @@ -554,13 +554,13 @@ def __repr__(self): back_populates='links') @classmethod - def printf(cls, *, label: str = 'printf'): + def printf(cls, /, *, label: str = 'printf'): return sa.case((cls.title != sa.null(), sa.func.printf('[%s](%s)', cls.title, cls.url)), else_=cls.url).label(label) @classmethod - def jsonf(cls, *, sort_keys: bool, label: str = 'jsonf'): + def jsonf(cls, /, *, sort_keys: bool, label: str = 'jsonf'): return json_object(scheme=cls.scheme, url=cls.url, title=cls.title, @@ -612,13 +612,13 @@ def __repr__(self): back_populates='timespan') @classmethod - def printf(cls, *, label: str = 'printf'): + def printf(cls, /, *, label: str = 'printf'): return sa.func.printf('%s-%s-%s/%s-%s-%s', cls.start_year, cls.start_month, cls.start_day, cls.end_year, cls.end_month, cls.end_day) @classmethod - def jsonf(cls, *, sort_keys: bool, label: str = 'jsonf'): + def jsonf(cls, /, *, sort_keys: bool, label: str = 'jsonf'): return json_object(start_year=cls.start_year, start_month=cls.start_month, start_day=cls.start_day, @@ -662,8 +662,8 @@ def __repr__(self): back_populates='sources') @classmethod - def printf(cls, bibfile, bibitem, - *, label: str = 'printf'): + def printf(cls, bibfile, bibitem, /, *, + label: str = 'printf'): return sa.case((sa.and_(cls.pages != sa.null(), cls.trigger != sa.null()), sa.func.printf('**%s:%s**:%s', bibfile.name, bibitem.bibkey, @@ -681,8 +681,8 @@ def printf(cls, bibfile, bibitem, ).label(label) @classmethod - def jsonf(cls, bibfile, bibitem, - *, sort_keys: bool, label: str = 'jsonf'): + def jsonf(cls, bibfile, bibitem, /, *, + sort_keys: bool, label: str = 'jsonf'): return json_object(bibfile=bibfile.name, bibkey=bibitem.bibkey, pages=cls.pages, @@ -787,12 +787,12 @@ def __repr__(self): back_populates='altnames') @classmethod - def printf(cls, *, label: str = 'printf'): + def printf(cls, /, *, label: str = 'printf'): full = sa.func.printf('%s [%s]', cls.name, cls.lang) return sa.case((cls.lang == '', cls.name), else_=full).label(label) @classmethod - def jsonf(cls, *, sort_keys: bool, label: str = 'jsonf'): + def jsonf(cls, /, *, sort_keys: bool, label: str = 'jsonf'): half = json_object(name=cls.name, lang=None, sort_keys_=sort_keys) @@ -939,15 +939,15 @@ def __repr__(self): back_populates='classificationrefs') @classmethod - def printf(cls, bibfile, bibitem, - *, label: str = 'printf'): + def printf(cls, bibfile, bibitem, /, *, + label: str = 'printf'): return sa.func.printf(sa.case((cls.pages != sa.null(), '**%s:%s**:%s'), else_='**%s:%s**'), bibfile.name, bibitem.bibkey, cls.pages).label(label) @classmethod - def jsonf(cls, bibfile, bibitem, - *, sort_keys: bool, label: str = 'jsonf'): + def jsonf(cls, bibfile, bibitem, /, *, + sort_keys: bool, label: str = 'jsonf'): return json_object(bibfile=bibfile.name, bibkey=bibitem.bibkey, pages=cls.pages, @@ -991,8 +991,8 @@ def __repr__(self): back_populates='endangerment') @classmethod - def jsonf(cls, source, bibfile, bibitem, - *, sort_keys: bool, label: str = 'jsonf'): + def jsonf(cls, source, bibfile, bibitem, /, *, + sort_keys: bool, label: str = 'jsonf'): source = json_object(name=source.name, bibfile=bibfile.name, bibkey=bibitem.bibkey, @@ -1063,8 +1063,8 @@ def __repr__(self): back_populates='source') @classmethod - def printf(cls, bibfile, bibitem, - *, label: str = 'printf'): + def printf(cls, bibfile, bibitem, /, *, + label: str = 'printf'): return sa.case((cls.bibitem_id == sa.null(), cls.name), else_=sa.func.printf('**%s:%s**:%s', bibfile.name, bibitem.bibkey, @@ -1098,7 +1098,7 @@ def __repr__(self): back_populates='ethnologue_comment') @classmethod - def jsonf(cls, *, sort_keys: bool, optional: bool = False, + def jsonf(cls, /, *, sort_keys: bool, optional: bool = False, label: str = 'jsonf'): mapping = json_object(isohid=cls.isohid, comment_type=cls.comment_type, @@ -1155,7 +1155,7 @@ def __repr__(self): back_populates='iso_retirement') @classmethod - def jsonf(cls, *, sort_keys: bool, + def jsonf(cls, /, *, sort_keys: bool, change_to=None, optional: bool = False, label: str = 'jsonf'): mapping = json_object(code=cls.code, diff --git a/treedb/queries.py b/treedb/queries.py index 9971856..0d3e408 100644 --- a/treedb/queries.py +++ b/treedb/queries.py @@ -160,8 +160,8 @@ def get_example_query(*, order_by: str = 'id') -> sa.sql.Select: return select_languoid -def add_order_by(select_languoid: sa.sql.Select, - *, order_by: str, column_for_path_order) -> sa.sql.Select: +def add_order_by(select_languoid: sa.sql.Select, /, *, + order_by: str, column_for_path_order) -> sa.sql.Select: if order_by in (True, None, 'id'): return select_languoid.order_by(Languoid.id) elif order_by == 'path': @@ -171,8 +171,8 @@ def add_order_by(select_languoid: sa.sql.Select, raise ValueError(f'{order_by=!r} not implemented') # pragma: no cover -def add_model_columns(select_languoid: sa.sql.Select, model, - *, add_outerjoin=None, label: str = '{name}', +def add_model_columns(select_languoid: sa.sql.Select, model, /, *, + add_outerjoin=None, label: str = '{name}', ignore: str = 'id') -> sa.sql.Select: columns = model.__table__.columns if ignore: @@ -187,12 +187,12 @@ def add_model_columns(select_languoid: sa.sql.Select, model, return select_languoid -def group_concat(x, *, separator: str = ', '): +def group_concat(x, /, *, separator: str = ', '): return sa.func.group_concat(x, separator) -def add_identifier(select_languoid: sa.sql.Select, site_name: str, - *, label: str) -> sa.sql.Select: +def add_identifier(select_languoid: sa.sql.Select, site_name: str, /, *, + label: str) -> sa.sql.Select: identifier = aliased(Identifier, name=f'ident_{site_name}') site = aliased(IdentifierSite, name=f'ident_{site_name}_site') label = label.format(site_name=site_name) @@ -203,8 +203,8 @@ def add_identifier(select_languoid: sa.sql.Select, site_name: str, identifier.languoid_id == Languoid.id))) -def add_classification_comment(select_languoid: sa.sql.Select, kind: str, - *, label: str, +def add_classification_comment(select_languoid: sa.sql.Select, kind: str, /, *, + label: str, bib_suffix: str = '_cr') -> sa.sql.Select: comment = aliased(ClassificationComment, name=f'cc_{kind}') label = label.format(kind=kind) @@ -214,8 +214,8 @@ def add_classification_comment(select_languoid: sa.sql.Select, kind: str, comment.languoid_id == Languoid.id))) -def add_classification_refs(select_languoid: sa.sql.Select, kind: str, - *, label: str, +def add_classification_refs(select_languoid: sa.sql.Select, kind: str, /, *, + label: str, bib_suffix: str = '_cr') -> sa.sql.Select: ref = aliased(ClassificationRef, name=f'cr_{kind}') bibfile = aliased(Bibfile, name=f'bibfile{bib_suffix}_{kind}') @@ -236,8 +236,8 @@ def add_classification_refs(select_languoid: sa.sql.Select, kind: str, return select_languoid.add_columns(refs) -def add_endangermentsource(select_languoid: sa.sql.Select, - *, label: str, +def add_endangermentsource(select_languoid: sa.sql.Select, /, *, + label: str, bib_suffix: str = '_e') -> sa.sql.Select: bibfile = aliased(Bibfile, name=f'bibfile{bib_suffix}') bibitem = aliased(Bibitem, name=f'bibitem{bib_suffix}') @@ -324,7 +324,7 @@ def get_json_query(*, limit: typing.Optional[int] = None, return select_json -def select_languoid_macroareas(languoid=Languoid, *, as_json: bool, +def select_languoid_macroareas(languoid=Languoid, /, *, as_json: bool, label: str = 'macroareas', alias: str = 'lang_ma') -> sa.sql.Select: name = languoid_macroarea.c.macroarea_name @@ -343,7 +343,7 @@ def select_languoid_macroareas(languoid=Languoid, *, as_json: bool, return select(macroareas.label(label)).label(label) -def select_languoid_countries(languoid=Languoid, *, as_json: bool, +def select_languoid_countries(languoid=Languoid, /, *, as_json: bool, label: str = 'countries', sort_keys: bool = False, alias: str = 'lang_country') -> sa.sql.Select: @@ -388,7 +388,7 @@ def select_languoid_links(languoid=Languoid, *, as_json: bool, return select(links.label(label)).label(label) -def select_languoid_timespan(languoid=Languoid, *, as_json: bool, +def select_languoid_timespan(languoid=Languoid, /, *, as_json: bool, label: str = 'timespan', sort_keys: bool = False) -> sa.sql.Select: return (select(Timespan.jsonf(sort_keys=sort_keys) if as_json else @@ -399,7 +399,7 @@ def select_languoid_timespan(languoid=Languoid, *, as_json: bool, .label(label)) -def select_languoid_sources(languoid=Languoid, *, as_json: bool, +def select_languoid_sources(languoid=Languoid, /, *, as_json: bool, provider_name: typing.Optional[str] = None, label: str = 'sources', sort_keys: bool = False, @@ -461,7 +461,7 @@ def select_languoid_sources(languoid=Languoid, *, as_json: bool, return select(sources.label(label)).label(label) -def select_languoid_altnames(languoid=Languoid, *, as_json: bool, +def select_languoid_altnames(languoid=Languoid, /, *, as_json: bool, provider_name: typing.Optional[str] = None, label: str = 'altnames', sort_keys: bool = False, @@ -515,7 +515,7 @@ def select_languoid_altnames(languoid=Languoid, *, as_json: bool, return select(altnames.label(label)).label(label) -def select_languoid_triggers(languoid=Languoid, *, as_json: bool, +def select_languoid_triggers(languoid=Languoid, /, *, as_json: bool, field_name: typing.Optional[str] = None, label: str = 'triggers', alias: str = 'lang_trigger') -> sa.sql.Select: @@ -562,8 +562,8 @@ def select_languoid_triggers(languoid=Languoid, *, as_json: bool, return select(triggers.label(label)).label(label) -def select_languoid_identifier(languoid=Languoid, - *, label: str = 'identifiers') -> sa.sql.Select: +def select_languoid_identifier(languoid=Languoid, /, *, + label: str = 'identifiers') -> sa.sql.Select: identifier = (select(IdentifierSite.name.label('site'), Identifier.identifier.label('identifier')) .select_from(Identifier) @@ -585,8 +585,8 @@ def select_languoid_identifier(languoid=Languoid, return select(identifier.label(label)).label(label) -def select_languoid_classification(languoid=Languoid, - *, label: str = 'classification', +def select_languoid_classification(languoid=Languoid, /, *, + label: str = 'classification', sort_keys: bool = False, bib_suffix: str = '_cr') -> sa.sql.Select: classification_comment = (select(ClassificationComment.kind.label('key'), @@ -636,8 +636,8 @@ def select_languoid_classification(languoid=Languoid, .label(label)) -def select_languoid_endangerment(languoid=Languoid, - *, label: str = 'endangerment', +def select_languoid_endangerment(languoid=Languoid, /, *, + label: str = 'endangerment', sort_keys: bool = False, bib_suffix: str = '_e') -> sa.sql.Select: bibitem = aliased(Bibitem, name=f'bibitem{bib_suffix}') @@ -655,8 +655,8 @@ def select_languoid_endangerment(languoid=Languoid, .label(label)) -def select_languoid_hh_ethnologue_comment(languoid=Languoid, - *, label: str = 'hh_ethnologue_comment', +def select_languoid_hh_ethnologue_comment(languoid=Languoid, /, *, + label: str = 'hh_ethnologue_comment', sort_keys: bool = False) -> sa.sql.Select: return (select(EthnologueComment .jsonf(sort_keys=sort_keys, label=label)) @@ -666,8 +666,8 @@ def select_languoid_hh_ethnologue_comment(languoid=Languoid, .label(label)) -def select_languoid_iso_retirement(languoid=Languoid, - *, label: str = 'iso_retirement', +def select_languoid_iso_retirement(languoid=Languoid, /, *, + label: str = 'iso_retirement', sort_keys: bool = False, alias: str = 'lang_irct', alias_label: str = 'change_to') -> sa.sql.Select: @@ -684,7 +684,7 @@ def select_languoid_iso_retirement(languoid=Languoid, .label(label)) -def select_iso_retirement_change_to(iso_retirement=IsoRetirement, *, +def select_iso_retirement_change_to(iso_retirement=IsoRetirement, /, *, as_json: bool, label: str, alias: str = 'lang_irct') -> sa.sql.Select: code = (select(IsoRetirementChangeTo.code) @@ -702,8 +702,8 @@ def select_iso_retirement_change_to(iso_retirement=IsoRetirement, *, def iterdescendants(parent_level: typing.Optional[str] = None, - child_level: typing.Optional[str] = None, - *, bind=_globals.ENGINE) -> typing.Iterator[typing.Tuple[str, typing.List[str]]]: + child_level: typing.Optional[str] = None, *, + bind=_globals.ENGINE) -> typing.Iterator[typing.Tuple[str, typing.List[str]]]: """Yield pairs of (parent id, sorted list of their descendant ids).""" # TODO: implement ancestors/descendants as sa.orm.relationship() # see https://bitbucket.org/zzzeek/sqlalchemy/issues/4165 diff --git a/treedb/raw/export.py b/treedb/raw/export.py index 392d4dc..ef28f62 100644 --- a/treedb/raw/export.py +++ b/treedb/raw/export.py @@ -77,7 +77,7 @@ def checksum(*, weak: bool = False, return f'{kind}:{hashobj.name}:{hashobj.hexdigest()}' -def write_raw_csv(filename=None, *, +def write_raw_csv(filename=None, /, *, dialect: str = csv23.DIALECT, encoding: str = csv23.ENCODING): """Write (path, section, option, line, value) rows to filename.""" if filename is None: @@ -100,7 +100,7 @@ def write_raw_csv(filename=None, *, dialect=dialect, encoding=encoding) -def write_files(root=_globals.ROOT, *, replace: bool = False, +def write_files(root=_globals.ROOT, /, *, replace: bool = False, dry_run: bool = False, require_nwritten: typing.Optional[int] = None, limit: typing.Optional[int] = None, diff --git a/treedb/raw/import_models.py b/treedb/raw/import_models.py index a6649fc..be601c4 100644 --- a/treedb/raw/import_models.py +++ b/treedb/raw/import_models.py @@ -23,11 +23,11 @@ class OptionMap(dict): model = Option - def __init__(self, items=(), *, conn): + def __init__(self, items=(), /, *, conn): super().__init__(items) self.insert = functools.partial(conn.execute, sa.insert(self.model)) - def __missing__(self, key): + def __missing__(self, key, /): log.debug('insert option: %r', key) section, option = key is_lines = _fields.is_lines(section, option) @@ -56,7 +56,7 @@ def __missing__(self, key): return result -def itervalues(cfg, file_id, *, option_map): +def itervalues(cfg, file_id, /, *, option_map): get_line = _tools.next_count(start=1) for section, sec in cfg.items(): for option, text in sec.items(): @@ -67,7 +67,7 @@ def itervalues(cfg, file_id, *, option_map): 'line': get_line(), 'value': v} -def main(root, *, conn): +def main(root, /, *, conn): insert_file = functools.partial(conn.execute, sa.insert(File)) option_id_is_lines = OptionMap(conn=conn) diff --git a/treedb/raw/models.py b/treedb/raw/models.py index f10d75b..566da22 100644 --- a/treedb/raw/models.py +++ b/treedb/raw/models.py @@ -34,7 +34,7 @@ class File: ' = glottocode'),) @classmethod - def path_depth(cls, label='path_depth'): + def path_depth(cls, /, *, label='path_depth'): return sa.func.floor((sa.func.length(cls.path) + 1) / 9).label(label) diff --git a/treedb/raw/records.py b/treedb/raw/records.py index 87390c0..0643845 100644 --- a/treedb/raw/records.py +++ b/treedb/raw/records.py @@ -96,7 +96,7 @@ def fetch_records(*, order_by: str = _globals.LANGUOID_ORDER, log.info('%s raw records total', f'{n:_d}') -def window_slices(key_column, *, size: int = WINDOWSIZE, +def window_slices(key_column, /, *, size: int = WINDOWSIZE, bind=_globals.ENGINE): """Yield where clause making function for key_column windows of size. @@ -122,7 +122,7 @@ def window_slices(key_column, *, size: int = WINDOWSIZE, yield lambda c, end=end: (c > end) -def iterkeys(key_column, *, size: int = WINDOWSIZE, +def iterkeys(key_column, /, *, size: int = WINDOWSIZE, bind=_globals.ENGINE): row_num = sa.func.row_number().over(order_by=key_column).label('row_num') select_all_keys = (sa.select(key_column.label('key'), row_num) diff --git a/treedb/settings.py b/treedb/settings.py index 8942aa8..f3c07d4 100644 --- a/treedb/settings.py +++ b/treedb/settings.py @@ -24,7 +24,7 @@ class ConfigParser(configparser.ConfigParser): @classmethod - def from_file(cls, path, *, required=False, default_repo_root): + def from_file(cls, path, /, *, required=False, default_repo_root): path = _tools.path_from_filename(path).resolve() defaults = {'here': path.parent.as_posix()} @@ -45,8 +45,8 @@ def set_default_repo_root(self, repo_root): self.set(*ROOT_OPTION, repo_root) -def configure(config_path=_globals.CONFIG, - *, engine=NOT_SET, root=NOT_SET, +def configure(config_path=_globals.CONFIG, /, *, + engine=NOT_SET, root=NOT_SET, loglevel=None, log_sql: bool = None, default_repo_root=_globals.DEFAULT_ROOT, title: typing.Optional[str] = None,