general: initial flake8 checks (for now manual)
fix fairly uncontroversial stuff in my.core like - line spacing, which isn't too annoying (e.g. unlike many inline whitespace checks that break vertical formatting) - unused imports/variables - too broad except
This commit is contained in:
parent
fd0c65d176
commit
016f28250b
19 changed files with 124 additions and 58 deletions
37
misc/.flake8-karlicoss
Normal file
37
misc/.flake8-karlicoss
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
[flake8]
|
||||||
|
ignore =
|
||||||
|
## these mess up vertical aligment
|
||||||
|
E126 # continuation line over-indented
|
||||||
|
E202 # whitespace before )
|
||||||
|
E203 # whitespace before ':' (e.g. in dict)
|
||||||
|
E221 # multiple spaces before operator
|
||||||
|
E241 # multiple spaces after ,
|
||||||
|
E251 # unexpected spaces after =
|
||||||
|
E261 # 2 spaces before comment. I actually think it's fine so TODO enable back later (TODO or not? still alignment)
|
||||||
|
E271 # multiple spaces after keyword
|
||||||
|
E272 # multiple spaces before keyword
|
||||||
|
##
|
||||||
|
E266 # 'too many leading # in the comment' -- this is just unnecessary pickiness, sometimes it's nice to format a comment
|
||||||
|
E302 # 2 blank lines
|
||||||
|
E501 # 'line too long' -- kinda annoying and the default 79 is shit anyway
|
||||||
|
E702 E704 # multiple statements on one line -- messes with : ... type declataions + sometimes asserts
|
||||||
|
E731 # suggests always using def instead of lambda
|
||||||
|
|
||||||
|
E402 # FIXME module level import -- we want it later
|
||||||
|
E252 # TODO later -- whitespace around equals?
|
||||||
|
# F541: f-string is missing placeholders -- perhaps too picky?
|
||||||
|
|
||||||
|
# F841 is pretty useful (unused variables). maybe worth making it an error on CI
|
||||||
|
|
||||||
|
|
||||||
|
# for imports: we might want to check these
|
||||||
|
# F401 good: unused imports
|
||||||
|
# E401: import order
|
||||||
|
# F811: redefinition of unused import
|
||||||
|
# todo from my.core import __NOT_HPI_MODULE__ this needs to be excluded from 'unused'
|
||||||
|
#
|
||||||
|
|
||||||
|
# as a reference:
|
||||||
|
# https://github.com/seanbreckenridge/cookiecutter-template/blob/master/%7B%7Bcookiecutter.module_name%7D%7D/setup.cfg
|
||||||
|
# and this https://github.com/karlicoss/HPI/pull/151
|
||||||
|
# find ./my | entr flake8 --ignore=E402,E501,E741,W503,E266,E302,E305,E203,E261,E252,E251,E221,W291,E225,E303,E702,E202,F841,E731,E306,E127 E722,E231 my | grep -v __NOT_HPI_MODULE__
|
|
@ -1,6 +1,6 @@
|
||||||
# this file only keeps the most common & critical types/utility functions
|
# this file only keeps the most common & critical types/utility functions
|
||||||
from .common import PathIsh, Paths, Json
|
from .common import get_files, PathIsh, Paths
|
||||||
from .common import get_files
|
from .common import Json
|
||||||
from .common import LazyLogger
|
from .common import LazyLogger
|
||||||
from .common import warn_if_empty
|
from .common import warn_if_empty
|
||||||
from .common import stat, Stats
|
from .common import stat, Stats
|
||||||
|
@ -8,11 +8,32 @@ from .common import datetime_naive, datetime_aware
|
||||||
from .common import assert_never
|
from .common import assert_never
|
||||||
|
|
||||||
from .cfg import make_config
|
from .cfg import make_config
|
||||||
|
|
||||||
from .util import __NOT_HPI_MODULE__
|
from .util import __NOT_HPI_MODULE__
|
||||||
|
|
||||||
from .error import Res, unwrap
|
from .error import Res, unwrap
|
||||||
|
|
||||||
|
|
||||||
# just for brevity in modules
|
# just for brevity in modules
|
||||||
|
# todo not sure about these.. maybe best to rely on regular imports.. perhaps compare?
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
'get_files', 'PathIsh', 'Paths',
|
||||||
|
'Json',
|
||||||
|
'LazyLogger',
|
||||||
|
'warn_if_empty',
|
||||||
|
'stat', 'Stats',
|
||||||
|
'datetime_aware', 'datetime_naive',
|
||||||
|
'assert_never',
|
||||||
|
|
||||||
|
'make_config',
|
||||||
|
|
||||||
|
'__NOT_HPI_MODULE__',
|
||||||
|
|
||||||
|
'Res', 'unwrap',
|
||||||
|
|
||||||
|
'dataclass', 'Path',
|
||||||
|
]
|
||||||
|
|
|
@ -17,7 +17,7 @@ import click
|
||||||
def mypy_cmd() -> Optional[Sequence[str]]:
|
def mypy_cmd() -> Optional[Sequence[str]]:
|
||||||
try:
|
try:
|
||||||
# preferably, use mypy from current python env
|
# preferably, use mypy from current python env
|
||||||
import mypy
|
import mypy # noqa: F401 fine not to use it
|
||||||
except ImportError:
|
except ImportError:
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
|
@ -63,6 +63,7 @@ def eprint(x: str) -> None:
|
||||||
def indent(x: str) -> str:
|
def indent(x: str) -> str:
|
||||||
return ''.join(' ' + l for l in x.splitlines(keepends=True))
|
return ''.join(' ' + l for l in x.splitlines(keepends=True))
|
||||||
|
|
||||||
|
|
||||||
OK = '✅'
|
OK = '✅'
|
||||||
OFF = '🔲'
|
OFF = '🔲'
|
||||||
|
|
||||||
|
@ -258,7 +259,7 @@ def modules_check(*, verbose: bool, list_all: bool, quick: bool, for_modules: Li
|
||||||
continue
|
continue
|
||||||
|
|
||||||
try:
|
try:
|
||||||
mod = importlib.import_module(m)
|
mod = importlib.import_module(m) # noqa: F841
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
# todo more specific command?
|
# todo more specific command?
|
||||||
error(f'{click.style("FAIL", fg="red")}: {m:<50} loading failed{vw}')
|
error(f'{click.style("FAIL", fg="red")}: {m:<50} loading failed{vw}')
|
||||||
|
@ -322,6 +323,7 @@ def tabulate_warnings() -> None:
|
||||||
'''
|
'''
|
||||||
import warnings
|
import warnings
|
||||||
orig = warnings.formatwarning
|
orig = warnings.formatwarning
|
||||||
|
|
||||||
def override(*args, **kwargs) -> str:
|
def override(*args, **kwargs) -> str:
|
||||||
res = orig(*args, **kwargs)
|
res = orig(*args, **kwargs)
|
||||||
return ''.join(' ' + x for x in res.splitlines(keepends=True))
|
return ''.join(' ' + x for x in res.splitlines(keepends=True))
|
||||||
|
|
|
@ -6,7 +6,7 @@ from typing import Optional
|
||||||
|
|
||||||
def disable_cachew() -> None:
|
def disable_cachew() -> None:
|
||||||
try:
|
try:
|
||||||
import cachew
|
import cachew # noqa: F401 # unused, it's fine
|
||||||
except ImportError:
|
except ImportError:
|
||||||
# nothing to disable
|
# nothing to disable
|
||||||
return
|
return
|
||||||
|
@ -19,7 +19,7 @@ from typing import Iterator
|
||||||
@contextmanager
|
@contextmanager
|
||||||
def disabled_cachew() -> Iterator[None]:
|
def disabled_cachew() -> Iterator[None]:
|
||||||
try:
|
try:
|
||||||
import cachew
|
import cachew # noqa: F401 # unused, it's fine
|
||||||
except ImportError:
|
except ImportError:
|
||||||
# nothing to disable
|
# nothing to disable
|
||||||
yield
|
yield
|
||||||
|
|
|
@ -12,7 +12,6 @@ from . import warnings as core_warnings
|
||||||
PathIsh = Union[Path, str]
|
PathIsh = Union[Path, str]
|
||||||
|
|
||||||
# TODO only used in tests? not sure if useful at all.
|
# TODO only used in tests? not sure if useful at all.
|
||||||
# TODO port annotations to kython?..
|
|
||||||
def import_file(p: PathIsh, name: Optional[str] = None) -> types.ModuleType:
|
def import_file(p: PathIsh, name: Optional[str] = None) -> types.ModuleType:
|
||||||
p = Path(p)
|
p = Path(p)
|
||||||
if name is None:
|
if name is None:
|
||||||
|
@ -48,11 +47,12 @@ T = TypeVar('T')
|
||||||
K = TypeVar('K')
|
K = TypeVar('K')
|
||||||
V = TypeVar('V')
|
V = TypeVar('V')
|
||||||
|
|
||||||
|
# TODO deprecate? more_itertools.one should be used
|
||||||
def the(l: Iterable[T]) -> T:
|
def the(l: Iterable[T]) -> T:
|
||||||
it = iter(l)
|
it = iter(l)
|
||||||
try:
|
try:
|
||||||
first = next(it)
|
first = next(it)
|
||||||
except StopIteration as ee:
|
except StopIteration:
|
||||||
raise RuntimeError('Empty iterator?')
|
raise RuntimeError('Empty iterator?')
|
||||||
assert all(e == first for e in it)
|
assert all(e == first for e in it)
|
||||||
return first
|
return first
|
||||||
|
@ -234,6 +234,7 @@ if TYPE_CHECKING:
|
||||||
# I guess, later just define pass through once this is fixed: https://github.com/python/typing/issues/270
|
# I guess, later just define pass through once this is fixed: https://github.com/python/typing/issues/270
|
||||||
# ok, that's actually a super nice 'pattern'
|
# ok, that's actually a super nice 'pattern'
|
||||||
F = TypeVar('F')
|
F = TypeVar('F')
|
||||||
|
|
||||||
class McachewType(Protocol):
|
class McachewType(Protocol):
|
||||||
def __call__(
|
def __call__(
|
||||||
self,
|
self,
|
||||||
|
@ -273,7 +274,7 @@ def mcachew(cache_path=_cache_path_dflt, **kwargs): # type: ignore[no-redef]
|
||||||
try:
|
try:
|
||||||
# check that it starts with 'hack' path
|
# check that it starts with 'hack' path
|
||||||
Path(cache_path).relative_to(_CACHE_DIR_NONE_HACK)
|
Path(cache_path).relative_to(_CACHE_DIR_NONE_HACK)
|
||||||
except:
|
except: # noqa: E722 bare except
|
||||||
pass # no action needed, doesn't start with 'hack' string
|
pass # no action needed, doesn't start with 'hack' string
|
||||||
else:
|
else:
|
||||||
# todo show warning? tbh unclear how to detect when user stopped using 'old' way and using suffix instead?
|
# todo show warning? tbh unclear how to detect when user stopped using 'old' way and using suffix instead?
|
||||||
|
@ -336,8 +337,7 @@ class classproperty(Generic[_R]):
|
||||||
# def __get__(self) -> _R:
|
# def __get__(self) -> _R:
|
||||||
# return self.f()
|
# return self.f()
|
||||||
|
|
||||||
# for now just serves documentation purposes... but one day might make it statically verifiable where possible?
|
# TODO deprecate in favor of datetime_aware
|
||||||
# TODO e.g. maybe use opaque mypy alias?
|
|
||||||
tzdatetime = datetime
|
tzdatetime = datetime
|
||||||
|
|
||||||
|
|
||||||
|
@ -352,6 +352,8 @@ def isoparse(s: str) -> tzdatetime:
|
||||||
s = s[:-1] + '+00:00'
|
s = s[:-1] + '+00:00'
|
||||||
return datetime.fromisoformat(s)
|
return datetime.fromisoformat(s)
|
||||||
|
|
||||||
|
|
||||||
|
# legacy import -- we should use compat directly instead
|
||||||
from .compat import Literal
|
from .compat import Literal
|
||||||
|
|
||||||
|
|
||||||
|
@ -412,6 +414,7 @@ def warn_if_empty(f: FI) -> FI: ...
|
||||||
|
|
||||||
def warn_if_empty(f):
|
def warn_if_empty(f):
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
|
|
||||||
@wraps(f)
|
@wraps(f)
|
||||||
def wrapped(*args, **kwargs):
|
def wrapped(*args, **kwargs):
|
||||||
res = f(*args, **kwargs)
|
res = f(*args, **kwargs)
|
||||||
|
@ -474,6 +477,7 @@ def _stat_iterable(it: Iterable[C], quick: bool=False) -> Any:
|
||||||
total = 0
|
total = 0
|
||||||
errors = 0
|
errors = 0
|
||||||
last = None
|
last = None
|
||||||
|
|
||||||
def funcit():
|
def funcit():
|
||||||
nonlocal errors, last, total
|
nonlocal errors, last, total
|
||||||
for x in it:
|
for x in it:
|
||||||
|
@ -542,7 +546,7 @@ def guess_datetime(x: Any) -> Optional[datetime]:
|
||||||
# todo hmm implement withoutexception..
|
# todo hmm implement withoutexception..
|
||||||
try:
|
try:
|
||||||
d = asdict(x)
|
d = asdict(x)
|
||||||
except:
|
except: # noqa: E722 bare except
|
||||||
return None
|
return None
|
||||||
for k, v in d.items():
|
for k, v in d.items():
|
||||||
if isinstance(v, datetime):
|
if isinstance(v, datetime):
|
||||||
|
@ -591,8 +595,8 @@ def asdict(thing: Any) -> Json:
|
||||||
raise TypeError(f'Could not convert object {thing} to dict')
|
raise TypeError(f'Could not convert object {thing} to dict')
|
||||||
|
|
||||||
|
|
||||||
|
# for now just serves documentation purposes... but one day might make it statically verifiable where possible?
|
||||||
|
# TODO e.g. maybe use opaque mypy alias?
|
||||||
datetime_naive = datetime
|
datetime_naive = datetime
|
||||||
datetime_aware = datetime
|
datetime_aware = datetime
|
||||||
|
|
||||||
|
|
|
@ -29,7 +29,7 @@ def pre_pip_dal_handler(
|
||||||
Specifying modules' dependencies in the config or in my/config/repos is deprecated!
|
Specifying modules' dependencies in the config or in my/config/repos is deprecated!
|
||||||
Please install {' '.join(requires)} as PIP packages (see the corresponding README instructions).
|
Please install {' '.join(requires)} as PIP packages (see the corresponding README instructions).
|
||||||
'''.strip(), stacklevel=2)
|
'''.strip(), stacklevel=2)
|
||||||
except ModuleNotFoundError as ee:
|
except ModuleNotFoundError:
|
||||||
dal = None
|
dal = None
|
||||||
|
|
||||||
if dal is None:
|
if dal is None:
|
||||||
|
@ -83,7 +83,9 @@ else:
|
||||||
from typing import TypeVar, Callable
|
from typing import TypeVar, Callable
|
||||||
Cl = TypeVar('Cl')
|
Cl = TypeVar('Cl')
|
||||||
R = TypeVar('R')
|
R = TypeVar('R')
|
||||||
|
|
||||||
def cached_property(f: Callable[[Cl], R]) -> R:
|
def cached_property(f: Callable[[Cl], R]) -> R:
|
||||||
|
import functools
|
||||||
return property(functools.lru_cache(maxsize=1)(f)) # type: ignore
|
return property(functools.lru_cache(maxsize=1)(f)) # type: ignore
|
||||||
del Cl
|
del Cl
|
||||||
del R
|
del R
|
||||||
|
|
|
@ -95,8 +95,6 @@ class Config(user_config):
|
||||||
return spec
|
return spec
|
||||||
return None
|
return None
|
||||||
|
|
||||||
enabled = self.enabled_modules
|
|
||||||
disabled = self.disabled_modules
|
|
||||||
on = matches(self.enabled_modules or [])
|
on = matches(self.enabled_modules or [])
|
||||||
off = matches(self.disabled_modules or [])
|
off = matches(self.disabled_modules or [])
|
||||||
|
|
||||||
|
|
|
@ -64,7 +64,7 @@ def sort_res_by(items: Iterable[Res[T]], key: Callable[[Any], K]) -> List[Res[T]
|
||||||
k: Optional[K]
|
k: Optional[K]
|
||||||
try:
|
try:
|
||||||
k = key(i)
|
k = key(i)
|
||||||
except Exception as e:
|
except Exception: # error white computing key? dunno, might be nice to handle...
|
||||||
k = None
|
k = None
|
||||||
group.append(i)
|
group.append(i)
|
||||||
if k is not None:
|
if k is not None:
|
||||||
|
@ -193,7 +193,6 @@ See {help_url} or check the corresponding module.py file for an example\
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def test_datetime_errors() -> None:
|
def test_datetime_errors() -> None:
|
||||||
import pytz
|
import pytz
|
||||||
dt_notz = datetime.now()
|
dt_notz = datetime.now()
|
||||||
|
@ -207,7 +206,6 @@ def test_datetime_errors() -> None:
|
||||||
e2 = RuntimeError(f'something something {dt} something else')
|
e2 = RuntimeError(f'something something {dt} something else')
|
||||||
assert extract_error_datetime(e2) == dt
|
assert extract_error_datetime(e2) == dt
|
||||||
|
|
||||||
|
|
||||||
e3 = RuntimeError(str(['one', '2019-11-27T08:56:00', 'three']))
|
e3 = RuntimeError(str(['one', '2019-11-27T08:56:00', 'three']))
|
||||||
assert extract_error_datetime(e3) is not None
|
assert extract_error_datetime(e3) is not None
|
||||||
|
|
||||||
|
|
|
@ -38,6 +38,7 @@ def fill(it: Iterable[Any], *, measurement: str, reset: bool=RESET_DEFAULT, dt_c
|
||||||
|
|
||||||
# TODO need to take schema here...
|
# TODO need to take schema here...
|
||||||
cache: Dict[str, bool] = {}
|
cache: Dict[str, bool] = {}
|
||||||
|
|
||||||
def good(f, v) -> bool:
|
def good(f, v) -> bool:
|
||||||
c = cache.get(f)
|
c = cache.get(f)
|
||||||
if c is not None:
|
if c is not None:
|
||||||
|
@ -79,7 +80,6 @@ def fill(it: Iterable[Any], *, measurement: str, reset: bool=RESET_DEFAULT, dt_c
|
||||||
fields=fields,
|
fields=fields,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
from more_itertools import chunked
|
from more_itertools import chunked
|
||||||
# "The optimal batch size is 5000 lines of line protocol."
|
# "The optimal batch size is 5000 lines of line protocol."
|
||||||
# some chunking is def necessary, otherwise it fails
|
# some chunking is def necessary, otherwise it fails
|
||||||
|
|
|
@ -92,6 +92,7 @@ class Wvalue(Zoomable):
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return 'WValue{' + repr(self.value) + '}'
|
return 'WValue{' + repr(self.value) + '}'
|
||||||
|
|
||||||
|
|
||||||
from typing import Tuple
|
from typing import Tuple
|
||||||
def _wrap(j, parent=None) -> Tuple[Zoomable, List[Zoomable]]:
|
def _wrap(j, parent=None) -> Tuple[Zoomable, List[Zoomable]]:
|
||||||
res: Zoomable
|
res: Zoomable
|
||||||
|
@ -118,6 +119,7 @@ def _wrap(j, parent=None) -> Tuple[Zoomable, List[Zoomable]]:
|
||||||
else:
|
else:
|
||||||
raise RuntimeError(f'Unexpected type: {type(j)} {j}')
|
raise RuntimeError(f'Unexpected type: {type(j)} {j}')
|
||||||
|
|
||||||
|
|
||||||
from contextlib import contextmanager
|
from contextlib import contextmanager
|
||||||
from typing import Iterator
|
from typing import Iterator
|
||||||
|
|
||||||
|
@ -142,8 +144,9 @@ Expected {c} to be fully consumed by the parser.
|
||||||
# TODO log?
|
# TODO log?
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
from typing import cast
|
from typing import cast
|
||||||
def test_unconsumed():
|
def test_unconsumed() -> None:
|
||||||
import pytest # type: ignore
|
import pytest # type: ignore
|
||||||
with pytest.raises(UnconsumedError):
|
with pytest.raises(UnconsumedError):
|
||||||
with wrap({'a': 1234}) as w:
|
with wrap({'a': 1234}) as w:
|
||||||
|
@ -155,7 +158,7 @@ def test_unconsumed():
|
||||||
w = cast(Wdict, w)
|
w = cast(Wdict, w)
|
||||||
d = w['c']['d'].zoom()
|
d = w['c']['d'].zoom()
|
||||||
|
|
||||||
def test_consumed():
|
def test_consumed() -> None:
|
||||||
with wrap({'a': 1234}) as w:
|
with wrap({'a': 1234}) as w:
|
||||||
w = cast(Wdict, w)
|
w = cast(Wdict, w)
|
||||||
a = w['a'].zoom()
|
a = w['a'].zoom()
|
||||||
|
@ -165,7 +168,7 @@ def test_consumed():
|
||||||
c = w['c'].zoom()
|
c = w['c'].zoom()
|
||||||
d = c['d'].zoom()
|
d = c['d'].zoom()
|
||||||
|
|
||||||
def test_types():
|
def test_types() -> None:
|
||||||
# (string, number, object, array, boolean or nul
|
# (string, number, object, array, boolean or nul
|
||||||
with wrap({'string': 'string', 'number': 3.14, 'boolean': True, 'null': None, 'list': [1, 2, 3]}) as w:
|
with wrap({'string': 'string', 'number': 3.14, 'boolean': True, 'null': None, 'list': [1, 2, 3]}) as w:
|
||||||
w = cast(Wdict, w)
|
w = cast(Wdict, w)
|
||||||
|
@ -176,14 +179,14 @@ def test_types():
|
||||||
for x in list(w['list'].zoom()): # TODO eh. how to avoid the extra list thing?
|
for x in list(w['list'].zoom()): # TODO eh. how to avoid the extra list thing?
|
||||||
x.consume()
|
x.consume()
|
||||||
|
|
||||||
def test_consume_all():
|
def test_consume_all() -> None:
|
||||||
with wrap({'aaa': {'bbb': {'hi': 123}}}) as w:
|
with wrap({'aaa': {'bbb': {'hi': 123}}}) as w:
|
||||||
w = cast(Wdict, w)
|
w = cast(Wdict, w)
|
||||||
aaa = w['aaa'].zoom()
|
aaa = w['aaa'].zoom()
|
||||||
aaa['bbb'].consume_all()
|
aaa['bbb'].consume_all()
|
||||||
|
|
||||||
|
|
||||||
def test_consume_few():
|
def test_consume_few() -> None:
|
||||||
import pytest
|
import pytest
|
||||||
pytest.skip('Will think about it later..')
|
pytest.skip('Will think about it later..')
|
||||||
with wrap({
|
with wrap({
|
||||||
|
|
|
@ -26,7 +26,7 @@ else:
|
||||||
|
|
||||||
|
|
||||||
def check_dateish(s) -> Iterable[str]:
|
def check_dateish(s) -> Iterable[str]:
|
||||||
import pandas as pd # type: ignore
|
import pandas as pd # type: ignore # noqa: F811 not actually a redefinition
|
||||||
ctype = s.dtype
|
ctype = s.dtype
|
||||||
if str(ctype).startswith('datetime64'):
|
if str(ctype).startswith('datetime64'):
|
||||||
return
|
return
|
||||||
|
@ -140,7 +140,7 @@ def as_dataframe(it: Iterable[Res[Any]], schema: Optional[Schema]=None) -> DataF
|
||||||
# https://github.com/pandas-dev/pandas/blob/fc9fdba6592bdb5d0d1147ce4d65639acd897565/pandas/core/frame.py#L562
|
# https://github.com/pandas-dev/pandas/blob/fc9fdba6592bdb5d0d1147ce4d65639acd897565/pandas/core/frame.py#L562
|
||||||
# same for NamedTuple -- seems that it takes whatever schema the first NT has
|
# same for NamedTuple -- seems that it takes whatever schema the first NT has
|
||||||
# so we need to convert each individually... sigh
|
# so we need to convert each individually... sigh
|
||||||
import pandas as pd
|
import pandas as pd # noqa: F811 not actually a redefinition
|
||||||
columns = None if schema is None else list(_as_columns(schema).keys())
|
columns = None if schema is None else list(_as_columns(schema).keys())
|
||||||
return pd.DataFrame(to_jsons(it), columns=columns)
|
return pd.DataFrame(to_jsons(it), columns=columns)
|
||||||
|
|
||||||
|
@ -148,7 +148,7 @@ def as_dataframe(it: Iterable[Res[Any]], schema: Optional[Schema]=None) -> DataF
|
||||||
def test_as_dataframe() -> None:
|
def test_as_dataframe() -> None:
|
||||||
import pytest
|
import pytest
|
||||||
it = (dict(i=i, s=f'str{i}') for i in range(10))
|
it = (dict(i=i, s=f'str{i}') for i in range(10))
|
||||||
with pytest.warns(UserWarning, match=r"No 'error' column") as record_warnings:
|
with pytest.warns(UserWarning, match=r"No 'error' column") as record_warnings: # noqa: F841
|
||||||
df = as_dataframe(it)
|
df = as_dataframe(it)
|
||||||
# todo test other error col policies
|
# todo test other error col policies
|
||||||
assert list(df.columns) == ['i', 's', 'error']
|
assert list(df.columns) == ['i', 's', 'error']
|
||||||
|
@ -156,6 +156,7 @@ def test_as_dataframe() -> None:
|
||||||
assert len(as_dataframe([])) == 0
|
assert len(as_dataframe([])) == 0
|
||||||
|
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class X:
|
class X:
|
||||||
x: int
|
x: int
|
||||||
|
|
|
@ -520,7 +520,6 @@ Will attempt to call iter() on the value""")
|
||||||
return itr
|
return itr
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# classes to use in tests, need to be defined at the top level
|
# classes to use in tests, need to be defined at the top level
|
||||||
# because of a mypy bug
|
# because of a mypy bug
|
||||||
class _Int(NamedTuple):
|
class _Int(NamedTuple):
|
||||||
|
@ -571,12 +570,10 @@ def test_order_key_multi_type() -> None:
|
||||||
for v in range(1, 6):
|
for v in range(1, 6):
|
||||||
yield _Int(v)
|
yield _Int(v)
|
||||||
|
|
||||||
|
|
||||||
def floaty_iter() -> Iterator[_Float]:
|
def floaty_iter() -> Iterator[_Float]:
|
||||||
for v in range(1, 6):
|
for v in range(1, 6):
|
||||||
yield _Float(float(v + 0.5))
|
yield _Float(float(v + 0.5))
|
||||||
|
|
||||||
|
|
||||||
res = list(select(itertools.chain(basic_iter(), floaty_iter()), order_key="x"))
|
res = list(select(itertools.chain(basic_iter(), floaty_iter()), order_key="x"))
|
||||||
assert res == [
|
assert res == [
|
||||||
_Int(1), _Float(1.5),
|
_Int(1), _Float(1.5),
|
||||||
|
|
|
@ -133,7 +133,8 @@ def _parse_range(
|
||||||
end_parser: Converter,
|
end_parser: Converter,
|
||||||
within_parser: Converter,
|
within_parser: Converter,
|
||||||
parsed_range: Optional[RangeTuple] = None,
|
parsed_range: Optional[RangeTuple] = None,
|
||||||
error_message: Optional[str] = None) -> Optional[RangeTuple]:
|
error_message: Optional[str] = None
|
||||||
|
) -> Optional[RangeTuple]:
|
||||||
|
|
||||||
if parsed_range is not None:
|
if parsed_range is not None:
|
||||||
return parsed_range
|
return parsed_range
|
||||||
|
@ -388,7 +389,6 @@ def test_filter_in_timeframe() -> None:
|
||||||
_A(x=datetime(2009, 5, 10, 4, 10, 1), y=5, z=10),
|
_A(x=datetime(2009, 5, 10, 4, 10, 1), y=5, z=10),
|
||||||
_B(y=datetime(year=2015, month=5, day=10, hour=4, minute=10, second=1))]
|
_B(y=datetime(year=2015, month=5, day=10, hour=4, minute=10, second=1))]
|
||||||
|
|
||||||
|
|
||||||
rng = RangeTuple(before=str(jan_1_2016), within="52w", after=None)
|
rng = RangeTuple(before=str(jan_1_2016), within="52w", after=None)
|
||||||
|
|
||||||
# from 2016, going back 52 weeks (about a year?)
|
# from 2016, going back 52 weeks (about a year?)
|
||||||
|
@ -438,8 +438,13 @@ def test_range_predicate() -> None:
|
||||||
|
|
||||||
# convert any float values to ints
|
# convert any float values to ints
|
||||||
coerce_int_parser = lambda o: int(float(o))
|
coerce_int_parser = lambda o: int(float(o))
|
||||||
int_filter_func = partial(_create_range_filter, attr_func=identity, end_parser=coerce_int_parser,
|
int_filter_func = partial(
|
||||||
within_parser=coerce_int_parser, value_coercion_func=coerce_int_parser)
|
_create_range_filter,
|
||||||
|
attr_func=identity,
|
||||||
|
end_parser=coerce_int_parser,
|
||||||
|
within_parser=coerce_int_parser,
|
||||||
|
value_coercion_func=coerce_int_parser,
|
||||||
|
)
|
||||||
|
|
||||||
# filter from 0 to 5
|
# filter from 0 to 5
|
||||||
rn: Optional[RangeTuple] = RangeTuple("0", "5", None)
|
rn: Optional[RangeTuple] = RangeTuple("0", "5", None)
|
||||||
|
@ -517,4 +522,3 @@ def test_parse_datetime_float() -> None:
|
||||||
|
|
||||||
# test parsing isoformat
|
# test parsing isoformat
|
||||||
assert dt.timestamp() == parse_datetime_float(str(dt))
|
assert dt.timestamp() == parse_datetime_float(str(dt))
|
||||||
|
|
||||||
|
|
|
@ -151,8 +151,6 @@ def dumps(
|
||||||
def test_serialize_fallback() -> None:
|
def test_serialize_fallback() -> None:
|
||||||
import json as jsn # dont cause possible conflicts with module code
|
import json as jsn # dont cause possible conflicts with module code
|
||||||
|
|
||||||
import pytest
|
|
||||||
|
|
||||||
# cant use a namedtuple here, since the default json.dump serializer
|
# cant use a namedtuple here, since the default json.dump serializer
|
||||||
# serializes namedtuples as tuples, which become arrays
|
# serializes namedtuples as tuples, which become arrays
|
||||||
# just test with an array of mixed objects
|
# just test with an array of mixed objects
|
||||||
|
@ -168,7 +166,6 @@ def test_serialize_fallback() -> None:
|
||||||
assert res == [5, 5.0]
|
assert res == [5, 5.0]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# this needs to be defined here to prevent a mypy bug
|
# this needs to be defined here to prevent a mypy bug
|
||||||
# see https://github.com/python/mypy/issues/7281
|
# see https://github.com/python/mypy/issues/7281
|
||||||
class _A(NamedTuple):
|
class _A(NamedTuple):
|
||||||
|
|
|
@ -3,9 +3,11 @@ Decorator to gracefully handle importing a data source, or warning
|
||||||
and yielding nothing (or a default) when its not available
|
and yielding nothing (or a default) when its not available
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from typing import Any, Iterator, TypeVar, Callable, Optional, Iterable, Any, cast
|
|
||||||
from my.core.warnings import medium, warn
|
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
|
from typing import Any, Iterator, TypeVar, Callable, Optional, Iterable
|
||||||
|
import warnings
|
||||||
|
|
||||||
|
from .warnings import medium
|
||||||
|
|
||||||
# The factory function may produce something that has data
|
# The factory function may produce something that has data
|
||||||
# similar to the shared model, but not exactly, so not
|
# similar to the shared model, but not exactly, so not
|
||||||
|
@ -55,7 +57,7 @@ def import_source(
|
||||||
medium(f"Module {factory_func.__qualname__} could not be imported, or isn't configured properly")
|
medium(f"Module {factory_func.__qualname__} could not be imported, or isn't configured properly")
|
||||||
else:
|
else:
|
||||||
medium(f"Module {module_name} ({factory_func.__qualname__}) could not be imported, or isn't configured properly")
|
medium(f"Module {module_name} ({factory_func.__qualname__}) could not be imported, or isn't configured properly")
|
||||||
warn(f"""If you don't want to use this module, to hide this message, add '{module_name}' to your core config disabled_modules in your config, like:
|
warnings.warn(f"""If you don't want to use this module, to hide this message, add '{module_name}' to your core config disabled_modules in your config, like:
|
||||||
|
|
||||||
class core:
|
class core:
|
||||||
disabled_modules = [{repr(module_name)}]
|
disabled_modules = [{repr(module_name)}]
|
||||||
|
@ -71,4 +73,3 @@ class core:
|
||||||
yield from default
|
yield from default
|
||||||
return wrapper
|
return wrapper
|
||||||
return decorator
|
return decorator
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ import importlib
|
||||||
import inspect
|
import inspect
|
||||||
import sys
|
import sys
|
||||||
import typing
|
import typing
|
||||||
from typing import Optional, Callable, Any, Iterator, Sequence, Dict
|
from typing import Optional, Callable, Any, Iterator, Sequence, Dict, List
|
||||||
|
|
||||||
from .common import StatsFun, Stats, stat
|
from .common import StatsFun, Stats, stat
|
||||||
|
|
||||||
|
@ -17,6 +17,7 @@ def guess_stats(module_name: str, quick: bool=False) -> Optional[StatsFun]:
|
||||||
providers = guess_data_providers(module_name)
|
providers = guess_data_providers(module_name)
|
||||||
if len(providers) == 0:
|
if len(providers) == 0:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def auto_stats() -> Stats:
|
def auto_stats() -> Stats:
|
||||||
return {k: stat(v, quick=quick) for k, v in providers.items()}
|
return {k: stat(v, quick=quick) for k, v in providers.items()}
|
||||||
return auto_stats
|
return auto_stats
|
||||||
|
@ -75,11 +76,11 @@ def test_is_data_provider() -> None:
|
||||||
lam = lambda: [1, 2]
|
lam = lambda: [1, 2]
|
||||||
assert not idp(lam)
|
assert not idp(lam)
|
||||||
|
|
||||||
def has_extra_args(count) -> typing.List[int]:
|
def has_extra_args(count) -> List[int]:
|
||||||
return list(range(count))
|
return list(range(count))
|
||||||
assert not idp(has_extra_args)
|
assert not idp(has_extra_args)
|
||||||
|
|
||||||
def has_return_type() -> typing.Sequence[str]:
|
def has_return_type() -> Sequence[str]:
|
||||||
return ['a', 'b', 'c']
|
return ['a', 'b', 'c']
|
||||||
assert idp(has_return_type)
|
assert idp(has_return_type)
|
||||||
|
|
||||||
|
@ -96,7 +97,6 @@ def test_is_data_provider() -> None:
|
||||||
assert not idp(producer_inputs)
|
assert not idp(producer_inputs)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# return any parameters the user is required to provide - those which don't have default values
|
# return any parameters the user is required to provide - those which don't have default values
|
||||||
def sig_required_params(sig: inspect.Signature) -> Iterator[inspect.Parameter]:
|
def sig_required_params(sig: inspect.Signature) -> Iterator[inspect.Parameter]:
|
||||||
for param in sig.parameters.values():
|
for param in sig.parameters.values():
|
||||||
|
|
|
@ -12,9 +12,6 @@ import warnings
|
||||||
import click
|
import click
|
||||||
|
|
||||||
|
|
||||||
# just bring in the scope of this module for convenience
|
|
||||||
from warnings import warn
|
|
||||||
|
|
||||||
def _colorize(x: str, color: Optional[str]=None) -> str:
|
def _colorize(x: str, color: Optional[str]=None) -> str:
|
||||||
if color is None:
|
if color is None:
|
||||||
return x
|
return x
|
||||||
|
@ -49,3 +46,7 @@ def high(message: str, *args, **kwargs) -> None:
|
||||||
'''
|
'''
|
||||||
kwargs['color'] = 'red'
|
kwargs['color'] = 'red'
|
||||||
_warn(message, *args, **kwargs)
|
_warn(message, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
# NOTE: deprecated -- legacy import
|
||||||
|
from warnings import warn
|
Loading…
Add table
Reference in a new issue