diff --git a/my/body/sleep/common.py b/my/body/sleep/common.py index e84c8d5..1100814 100644 --- a/my/body/sleep/common.py +++ b/my/body/sleep/common.py @@ -7,7 +7,7 @@ class Combine: self.modules = modules @cdf - def dataframe(self, with_temperature: bool=True) -> DataFrameT: + def dataframe(self, *, with_temperature: bool=True) -> DataFrameT: import pandas as pd # todo include 'source'? df = pd.concat([m.dataframe() for m in self.modules]) diff --git a/my/core/__main__.py b/my/core/__main__.py index 986b05f..d3c0cc7 100644 --- a/my/core/__main__.py +++ b/my/core/__main__.py @@ -438,7 +438,7 @@ def _ui_getchar_pick(choices: Sequence[str], prompt: str = 'Select from: ') -> i return result_map[ch] -def _locate_functions_or_prompt(qualified_names: List[str], prompt: bool = True) -> Iterable[Callable[..., Any]]: +def _locate_functions_or_prompt(qualified_names: List[str], *, prompt: bool = True) -> Iterable[Callable[..., Any]]: from .query import QueryException, locate_qualified_function from .stats import is_data_provider @@ -588,7 +588,7 @@ def query_hpi_functions( @click.group() @click.option("--debug", is_flag=True, default=False, help="Show debug logs") -def main(debug: bool) -> None: +def main(*, debug: bool) -> None: ''' Human Programming Interface @@ -637,7 +637,7 @@ def _module_autocomplete(ctx: click.Context, args: Sequence[str], incomplete: st @click.option('-q', '--quick', is_flag=True, help='Only run partial checks (first 100 items)') @click.option('-S', '--skip-config-check', 'skip_conf', is_flag=True, help='Skip configuration check') @click.argument('MODULE', nargs=-1, required=False, shell_complete=_module_autocomplete) -def doctor_cmd(verbose: bool, list_all: bool, quick: bool, skip_conf: bool, module: Sequence[str]) -> None: +def doctor_cmd(*, verbose: bool, list_all: bool, quick: bool, skip_conf: bool, module: Sequence[str]) -> None: ''' Run various checks @@ -671,7 +671,7 @@ def config_create_cmd() -> None: @main.command(name='modules', short_help='list available modules') @click.option('--all', 'list_all', is_flag=True, help='List all modules, including disabled') -def module_cmd(list_all: bool) -> None: +def module_cmd(*, list_all: bool) -> None: '''List available modules''' list_modules(list_all=list_all) @@ -684,7 +684,7 @@ def module_grp() -> None: @module_grp.command(name='requires', short_help='print module reqs') @click.argument('MODULES', shell_complete=_module_autocomplete, nargs=-1, required=True) -def module_requires_cmd(modules: Sequence[str]) -> None: +def module_requires_cmd(*, modules: Sequence[str]) -> None: ''' Print MODULES requirements @@ -701,7 +701,7 @@ def module_requires_cmd(modules: Sequence[str]) -> None: is_flag=True, help='Bypass PEP 668 and install dependencies into the system-wide python package directory.') @click.argument('MODULES', shell_complete=_module_autocomplete, nargs=-1, required=True) -def module_install_cmd(user: bool, parallel: bool, break_system_packages: bool, modules: Sequence[str]) -> None: +def module_install_cmd(*, user: bool, parallel: bool, break_system_packages: bool, modules: Sequence[str]) -> None: ''' Install dependencies for modules using pip @@ -782,6 +782,7 @@ def module_install_cmd(user: bool, parallel: bool, break_system_packages: bool, help='ignore any errors returned as objects from the functions') @click.argument('FUNCTION_NAME', nargs=-1, required=True, shell_complete=_module_autocomplete) def query_cmd( + *, function_name: Sequence[str], output: str, stream: bool, diff --git a/my/core/common.py b/my/core/common.py index ba4ce6b..b97866f 100644 --- a/my/core/common.py +++ b/my/core/common.py @@ -26,10 +26,11 @@ Paths = Union[Sequence[PathIsh], PathIsh] DEFAULT_GLOB = '*' def get_files( - pp: Paths, - glob: str=DEFAULT_GLOB, - sort: bool=True, - guess_compression: bool=True, + pp: Paths, + glob: str=DEFAULT_GLOB, + *, + sort: bool=True, + guess_compression: bool=True, ) -> Tuple[Path, ...]: """ Helper function to avoid boilerplate. diff --git a/my/core/denylist.py b/my/core/denylist.py index 7ca0ddf..92faf2c 100644 --- a/my/core/denylist.py +++ b/my/core/denylist.py @@ -96,6 +96,7 @@ class DenyList: def filter( self, itr: Iterator[T], + *, invert: bool = False, ) -> Iterator[T]: denyf = functools.partial(self._allow, deny_map=self.load()) @@ -103,7 +104,7 @@ class DenyList: return filter(lambda x: not denyf(x), itr) return filter(denyf, itr) - def deny(self, key: str, value: Any, write: bool = False) -> None: + def deny(self, key: str, value: Any, *, write: bool = False) -> None: ''' add a key/value pair to the denylist ''' @@ -111,7 +112,7 @@ class DenyList: self._load() self._deny_raw({key: self._stringify_value(value)}, write=write) - def _deny_raw(self, data: Dict[str, Any], write: bool = False) -> None: + def _deny_raw(self, data: Dict[str, Any], *, write: bool = False) -> None: self._deny_raw_list.append(data) if write: self.write() diff --git a/my/core/influxdb.py b/my/core/influxdb.py index b1d6b9b..25eeba1 100644 --- a/my/core/influxdb.py +++ b/my/core/influxdb.py @@ -135,7 +135,7 @@ def main() -> None: @main.command(name='populate', short_help='populate influxdb') @click.option('--reset', is_flag=True, help='Reset Influx measurements before inserting', show_default=True) @click.argument('FUNCTION_NAME', type=str, required=True) -def populate(function_name: str, reset: bool) -> None: +def populate(*, function_name: str, reset: bool) -> None: from .__main__ import _locate_functions_or_prompt [provider] = list(_locate_functions_or_prompt([function_name])) # todo could have a non-interactive version which populates from all data sources for the provider? diff --git a/my/core/konsume.py b/my/core/konsume.py index ac1b100..0e4a2fe 100644 --- a/my/core/konsume.py +++ b/my/core/konsume.py @@ -131,7 +131,7 @@ class UnconsumedError(Exception): # TODO think about error policy later... @contextmanager -def wrap(j, throw=True) -> Iterator[Zoomable]: +def wrap(j, *, throw=True) -> Iterator[Zoomable]: w, children = _wrap(j) yield w diff --git a/my/core/query.py b/my/core/query.py index bc7e222..daf702d 100644 --- a/my/core/query.py +++ b/my/core/query.py @@ -131,11 +131,12 @@ def attribute_func(obj: T, where: Where, default: Optional[U] = None) -> Optiona def _generate_order_by_func( - obj_res: Res[T], - key: Optional[str] = None, - where_function: Optional[Where] = None, - default: Optional[U] = None, - force_unsortable: bool = False, + obj_res: Res[T], + *, + key: Optional[str] = None, + where_function: Optional[Where] = None, + default: Optional[U] = None, + force_unsortable: bool = False, ) -> Optional[OrderFunc]: """ Accepts an object Res[T] (Instance of some class or Exception) @@ -274,6 +275,7 @@ def _wrap_unsorted(itr: Iterator[ET], orderfunc: OrderFunc) -> Tuple[Iterator[Un # the second being items for which orderfunc returned a non-none value def _handle_unsorted( itr: Iterator[ET], + *, orderfunc: OrderFunc, drop_unsorted: bool, wrap_unsorted: bool @@ -503,7 +505,12 @@ Will attempt to call iter() on the value""") # note: can't just attach sort unsortable values in the same iterable as the # other items because they don't have any lookups for order_key or functions # to handle items in the order_by_lookup dictionary - unsortable, itr = _handle_unsorted(itr, order_by_chosen, drop_unsorted, wrap_unsorted) + unsortable, itr = _handle_unsorted( + itr, + orderfunc=order_by_chosen, + drop_unsorted=drop_unsorted, + wrap_unsorted=wrap_unsorted, + ) # run the sort, with the computed order by function itr = iter(sorted(itr, key=order_by_chosen, reverse=reverse)) # type: ignore[arg-type] diff --git a/my/core/stats.py b/my/core/stats.py index 85c2a99..4c9fb0c 100644 --- a/my/core/stats.py +++ b/my/core/stats.py @@ -30,7 +30,7 @@ Stats = Dict[str, Any] class StatsFun(Protocol): - def __call__(self, quick: bool = False) -> Stats: ... + def __call__(self, *, quick: bool = False) -> Stats: ... # global state that turns on/off quick stats @@ -176,7 +176,7 @@ def guess_stats(module: ModuleType) -> Optional[StatsFun]: if len(providers) == 0: return None - def auto_stats(quick: bool = False) -> Stats: + def auto_stats(*, quick: bool = False) -> Stats: res = {} for k, v in providers.items(): res.update(stat(v, quick=quick, name=k)) @@ -355,7 +355,7 @@ def _stat_item(item): return _guess_datetime(item) -def _stat_iterable(it: Iterable[Any], quick: bool = False) -> Stats: +def _stat_iterable(it: Iterable[Any], *, quick: bool = False) -> Stats: from more_itertools import first, ilen, take # todo not sure if there is something in more_itertools to compute this? diff --git a/my/core/structure.py b/my/core/structure.py index df25e37..149a22a 100644 --- a/my/core/structure.py +++ b/my/core/structure.py @@ -12,7 +12,7 @@ from .logging import make_logger logger = make_logger(__name__, level="info") -def _structure_exists(base_dir: Path, paths: Sequence[str], partial: bool = False) -> bool: +def _structure_exists(base_dir: Path, paths: Sequence[str], *, partial: bool = False) -> bool: """ Helper function for match_structure to check if all subpaths exist at some base directory diff --git a/my/core/utils/concurrent.py b/my/core/utils/concurrent.py index 3553cd9..5f11ab0 100644 --- a/my/core/utils/concurrent.py +++ b/my/core/utils/concurrent.py @@ -47,5 +47,5 @@ class DummyExecutor(Executor): return f - def shutdown(self, wait: bool = True, **kwargs) -> None: + def shutdown(self, wait: bool = True, **kwargs) -> None: # noqa: FBT001,FBT002 self._shutdown = True diff --git a/my/endomondo.py b/my/endomondo.py index d314e97..1d7acc2 100644 --- a/my/endomondo.py +++ b/my/endomondo.py @@ -44,7 +44,7 @@ def workouts() -> Iterable[Res[Workout]]: from .core.pandas import check_dataframe, DataFrameT @check_dataframe -def dataframe(defensive: bool=True) -> DataFrameT: +def dataframe(*, defensive: bool=True) -> DataFrameT: def it(): for w in workouts(): if isinstance(w, Exception): diff --git a/my/google/takeout/parser.py b/my/google/takeout/parser.py index c4e5682..258ab96 100644 --- a/my/google/takeout/parser.py +++ b/my/google/takeout/parser.py @@ -91,7 +91,7 @@ def _cachew_depends_on() -> List[str]: # ResultsType is a Union of all of the models in google_takeout_parser @mcachew(depends_on=_cachew_depends_on, logger=logger, force_file=True) -def events(disable_takeout_cache: bool = DISABLE_TAKEOUT_CACHE) -> CacheResults: +def events(disable_takeout_cache: bool = DISABLE_TAKEOUT_CACHE) -> CacheResults: # noqa: FBT001 error_policy = config.error_policy count = 0 emitted = GoogleEventSet() diff --git a/my/jawbone/__init__.py b/my/jawbone/__init__.py index 5d43296..1706a54 100644 --- a/my/jawbone/__init__.py +++ b/my/jawbone/__init__.py @@ -174,7 +174,7 @@ def hhmm(time: datetime): # return fromstart / tick -def plot_one(sleep: SleepEntry, fig, axes, xlims=None, showtext=True): +def plot_one(sleep: SleepEntry, fig, axes, xlims=None, *, showtext=True): import matplotlib.dates as mdates # type: ignore[import-not-found] span = sleep.completed - sleep.created diff --git a/my/location/fallback/all.py b/my/location/fallback/all.py index 0c7b8cd..a5daa05 100644 --- a/my/location/fallback/all.py +++ b/my/location/fallback/all.py @@ -24,7 +24,7 @@ def fallback_estimators() -> Iterator[LocationEstimator]: yield _home_estimate -def estimate_location(dt: DateExact, first_match: bool=False, under_accuracy: Optional[int] = None) -> FallbackLocation: +def estimate_location(dt: DateExact, *, first_match: bool=False, under_accuracy: Optional[int] = None) -> FallbackLocation: loc = estimate_from(dt, estimators=list(fallback_estimators()), first_match=first_match, under_accuracy=under_accuracy) # should never happen if the user has home configured if loc is None: diff --git a/my/location/fallback/common.py b/my/location/fallback/common.py index fd508c6..13bc603 100644 --- a/my/location/fallback/common.py +++ b/my/location/fallback/common.py @@ -18,7 +18,7 @@ class FallbackLocation(LocationProtocol): elevation: Optional[float] = None datasource: Optional[str] = None # which module provided this, useful for debugging - def to_location(self, end: bool = False) -> Location: + def to_location(self, *, end: bool = False) -> Location: ''' by default the start date is used for the location If end is True, the start date + duration is used diff --git a/my/tests/tz.py b/my/tests/tz.py index db88278..92d8f3b 100644 --- a/my/tests/tz.py +++ b/my/tests/tz.py @@ -18,7 +18,7 @@ def getzone(dt: datetime) -> str: @pytest.mark.parametrize('fast', [False, True]) -def test_iter_tzs(fast: bool, config) -> None: +def test_iter_tzs(*, fast: bool, config) -> None: # TODO hmm.. maybe need to make sure we start with empty config? config.time.tz.via_location.fast = fast diff --git a/my/time/tz/via_location.py b/my/time/tz/via_location.py index d74bdc3..156a5db 100644 --- a/my/time/tz/via_location.py +++ b/my/time/tz/via_location.py @@ -94,7 +94,7 @@ logger = make_logger(__name__) @lru_cache(None) -def _timezone_finder(fast: bool) -> Any: +def _timezone_finder(*, fast: bool) -> Any: if fast: # less precise, but faster from timezonefinder import TimezoneFinderL as Finder @@ -304,7 +304,7 @@ def localize(dt: datetime) -> datetime_aware: return tz.localize(dt) -def stats(quick: bool = False) -> Stats: +def stats(*, quick: bool = False) -> Stats: if quick: prev, config.sort_locations = config.sort_locations, False res = {'first': next(_iter_local_dates())} diff --git a/my/topcoder.py b/my/topcoder.py index 8e39252..07f71be 100644 --- a/my/topcoder.py +++ b/my/topcoder.py @@ -58,7 +58,7 @@ def _parse_one(p: Path) -> Iterator[Res[Competition]]: h.pop_if_primitive('version', 'id') h = h.zoom('result') - h.check('success', True) + h.check('success', expected=True) h.check('status', 200) h.pop_if_primitive('metadata') diff --git a/ruff.toml b/ruff.toml index dda279b..a8af399 100644 --- a/ruff.toml +++ b/ruff.toml @@ -1,10 +1,11 @@ target-version = "py38" # NOTE: inferred from pyproject.toml if present lint.extend-select = [ - "F", # flakes rules -- default, but extend just in case - "E", # pycodestyle -- default, but extend just in case - "C4", # flake8-comprehensions -- unnecessary list/map/dict calls - "UP", # detect deprecated python stdlib stuff + "F", # flakes rules -- default, but extend just in case + "E", # pycodestyle -- default, but extend just in case + "C4", # flake8-comprehensions -- unnecessary list/map/dict calls + "UP", # detect deprecated python stdlib stuff + "FBT", # detect use of boolean arguments ] lint.ignore = [